[ZJOI2016]小星星&[SHOI2016]黑暗前的幻想乡(容斥)
这两道题思路比较像,所以把他们放到一块。
[ZJOI2016]小星星
题目描述
小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有n颗小星星,用m条彩色的细线串了起来,每条细线连着两颗小星星。
有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了n-1条细线,但通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小Y找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。小Y想知道有多少种可能的对应方式。
只有你告诉了她正确的答案,她才会把小饰品做为礼物送给你呢。
题解
做容斥题有一个基本模型,就是有一个限制,我们直接在转移或者统计复杂度过高,但如果把它放宽一点的话复杂度会降低许多。
然后总的条件数也支持2^n枚举,就可以去考虑容斥。
这个题是说,有一个n个点的无向图和n个点的一棵树,问有多少种一一对应的映射使得在树中有的边,图中也有。
看到树可以联想树形dp,因为我们要求一一对应,所以我们可以考虑设dp[i][j][s]表示以i为根的子树,i对应了图中的j,i子树对应了图中的集合s的方案数。
转移还是比较简单的。
我们观察到这个算法复杂度瓶颈在于枚举s,所以我们考虑能不能去掉。
去掉之后会出现树中的多个点对应了图中的一个点,方案数会算多。
怎么办?这个形式其实已经很明显了,直接套用容斥公式算就好了。
2^n枚举图中选那些点,然后做二维的树形dp就好了。
代码
#include<iostream>
#include<cstdio>
#define N 18
#define R register
using namespace std;
typedef long long ll;
int n,m,tot,head[N],cou[<<N];
ll dp[N][N],ans;
bool a[N][N],jin[N];
inline int rd(){
int x=;char c=getchar();bool f=;
while(!isdigit(c)){if(c=='-')f=;c=getchar();}
while(isdigit(c)){x=(x<<)+(x<<)+(c^);c=getchar();}
return f?-x:x;
}
struct edge{int n,to;}e[N*N*];
inline void add(int u,int v){
e[++tot].n=head[u];e[tot].to=v;head[u]=tot;
}
void dfs(int u,int fa){
for(R int i=;i<=n;++i)if(!jin[i])dp[u][i]=;else dp[u][i]=;
for(R int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
int v=e[i].to;dfs(v,u);
for(R int j=;j<=n;++j)if(!jin[j]){
ll num=;
for(R int k=;k<=n;++k)if(a[j][k]&&!jin[k])num+=dp[v][k];
dp[u][j]*=num;
}
}
}
int main(){
n=rd();m=rd();int x,y;
for(R int i=;i<=m;++i){
x=rd();y=rd();
a[x][y]=a[y][x]=;
}
for(R int i=;i<n;++i){x=rd();y=rd();add(x,y);add(y,x);}
for(R int i=;i<(<<n);++i){
cou[i]=cou[i>>]+(i&);
for(R int j=;j<=n;++j)jin[j]=(i&(<<j-))!=;
dfs(,);
ll num=;
for(R int j=;j<=n;++j)num+=dp[][j];
if(cou[i]&)ans-=num;else ans+=num;
}
cout<<ans;
return ;
}
[SHOI2016]黑暗前的幻想乡
题目描述
四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖怪涌入了幻想乡,扰乱了幻想乡昔日的秩序。但是幻想乡的建制派妖怪(人类)博丽灵梦和八云紫等人整日高谈所有妖怪平等,幻想乡多元化等等,对于幻想乡目前面临的种种大问题却给不出合理的解决方案。
风见幽香是幻想乡里少有的意识到了问题严重性的大妖怪。她这次勇敢地站了出来参加幻想乡大选,提出包括在幻想乡边境建墙(并让人类出钱),大力开展基础设施建设挽回失业率等一系列方案,成为了大选年出人意料的黑马并顺利地当上了幻想乡的大统领。
幽香上台以后,第一项措施就是要修建幻想乡的公路。幻想乡一共有 nn 个城市,之前原来没有任何路。幽香向选民承诺要减税,所以她打算只修 n-1n−1条公路将这些城市连接起来。但是幻想乡有正好 n-1n−1 个建筑公司,每个建筑公司都想在修路地过程中获得一些好处。虽然这些建筑公司在选举前没有给幽香钱,幽香还是打算和他们搞好关系,因为她还指望他们帮她建墙。所以她打算让每个建筑公司都负责一条路来修。
每个建筑公司都告诉了幽香自己有能力负责修建的路是哪些城市之间的。所以幽香打算 n - 1n−1条能够连接幻想乡所有城市的边,然后每条边都交给一个能够负责该边的建筑公司修建,并且每个建筑公司都恰好修建一条边。
幽香现在想要知道一共有多少种可能的方案呢?两个方案不同当且仅当它们要么修的边的集合不同,要么边的分配方式不同。
题解
这题和上一题相似,有两个限制。
上一题是要满足树中和图中都要有某条边。
这题是要满足我们的生成树中,既要有n个不同的点,还要满足每种颜色的边个出现一次。
如果我们去掉第二个限制,那就变成了生成树计数问题,套用矩阵树定理即可。
然后我们发现面前的这个问题还是可以套用容斥公式直接计算的,于是这道题被愉快的解决了。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 18
using namespace std;
typedef long long ll;
const int mod=1e9+;
ll a[N][N];
int n,m,cou[<<N];
bool tag[N];
inline int rd(){
int x=;char c=getchar();bool f=;
while(!isdigit(c)){if(c=='-')f=;c=getchar();}
while(isdigit(c)){x=(x<<)+(x<<)+(c^);c=getchar();}
return f?-x:x;
}
inline ll power(ll x,ll y){
ll ans=;
while(y){if(y&)ans=ans*x%mod;x=x*x%mod;y>>=;}
return ans;
}
struct node{int x,y;};
vector<node>vec[N];
inline ll ni(ll x){return power(x,mod-);}
inline ll gauss(ll tot){
ll ans=;
for(int i=;i<=tot;++i)
for(int j=i+;j<=tot;++j){
ll t=a[j][i]*ni(a[i][i])%mod;
for(int k=i;k<=tot;++k)a[j][k]=((a[j][k]-t*a[i][k])%mod+mod)%mod;
}
for(int i=;i<=tot;++i)ans=(ans*a[i][i]%mod+mod)%mod;
return ans;
}
inline ll work(){
memset(a,,sizeof(a));
for(int i=;i<n;++i)if(tag[i]){
for(int j=;j<vec[i].size();++j){
int x=vec[i][j].x,y=vec[i][j].y;
a[x][x]++;a[y][y]++;a[x][y]--;a[y][x]--;
}
}
return gauss(n-);
}
int main(){
n=rd();int x,y;
for(int i=;i<n;++i){
m=rd();
for(int j=;j<=m;++j){
x=rd();y=rd();vec[i].push_back(node{x,y});
}
}
ll ans=;
for(int i=;i<(<<n-);++i){
cou[i]=cou[i>>]+(i&);
for(int j=;j<=n-;++j)tag[j]=(i&(<<j-))==;
if(cou[i]&)ans-=work();else ans+=work();
ans=(ans%mod+mod)%mod;
}
cout<<ans;
return ;
}
[ZJOI2016]小星星&[SHOI2016]黑暗前的幻想乡(容斥)的更多相关文章
- 【BZOJ4596】[Shoi2016]黑暗前的幻想乡 容斥+矩阵树定理
[BZOJ4596][Shoi2016]黑暗前的幻想乡 Description 幽香上台以后,第一项措施就是要修建幻想乡的公路.幻想乡有 N 个城市,之间原来没有任何路.幽香向选民承诺要减税,所以她打 ...
- BZOJ 4596: [Shoi2016]黑暗前的幻想乡(容斥+Matrix_Tree)
传送门 解题思路 看到计数想容斥--\(from\) \(shadowice1984\)大爷.首先求出原图的生成树个数比较容易,直接上矩阵树定理,但这样会多算一点东西,会把\(n-2\)个公司的多算进 ...
- 洛谷 P4336 黑暗前的幻想乡 —— 容斥+矩阵树定理
题目:https://www.luogu.org/problemnew/show/P4336 当作考试题了,然而没想出来,呵呵. 其实不是二分图完美匹配方案数,而是矩阵树定理+容斥... 就是先放上所 ...
- bzoj 4596 [Shoi2016]黑暗前的幻想乡 矩阵树定理+容斥
4596: [Shoi2016]黑暗前的幻想乡 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 559 Solved: 325[Submit][Sta ...
- bzoj4596[Shoi2016]黑暗前的幻想乡 Matrix定理+容斥原理
4596: [Shoi2016]黑暗前的幻想乡 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 464 Solved: 264[Submit][Sta ...
- P4336 [SHOI2016]黑暗前的幻想乡
P4336 [SHOI2016]黑暗前的幻想乡 矩阵树定理(高斯消元+乘法逆元)+容斥 ans=总方案数 -(公司1未参加方案数 ∪ 公司2未参加方案数 ∪ 公司3未参加方案数 ∪ ...... ∪ ...
- 【BZOJ 4596】 4596: [Shoi2016]黑暗前的幻想乡 (容斥原理+矩阵树定理)
4596: [Shoi2016]黑暗前的幻想乡 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 324 Solved: 187 Description ...
- bzoj4596/luoguP4336 [SHOI2016]黑暗前的幻想乡(矩阵树定理,容斥)
bzoj4596/luoguP4336 [SHOI2016]黑暗前的幻想乡(矩阵树定理,容斥) bzoj Luogu 题解时间 看一看数据范围,求生成树个数毫无疑问直接上矩阵树定理. 但是要求每条边都 ...
- BZOJ4596: [Shoi2016]黑暗前的幻想乡
Description 四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖 怪涌入了幻想乡,扰乱了幻想乡昔日的秩序.但是幻想乡的建制派妖怪(人类) 博丽灵梦和八云紫等人整日高谈所有妖怪 ...
随机推荐
- 使用fetch代替ajax请求 post传递方式
let postData = {a:'b'}; fetch('http://data.xxx.com/Admin/Login/login', { method: 'POST', mode: 'cors ...
- [转帖]Centos7 yum安装Chrome浏览器
Centos7 yum安装Chrome浏览器 https://www.cnblogs.com/ianduin/p/8727333.html以及https://blog.csdn.net/libaine ...
- vue 项目使用 webpack 构建自动获取电脑ip地址
1.开发 H5 时移动端,经常会使用真机进行调试本地环境.webpack 配置服务器好多脚手架写的都是固定的,而在团队开发中需要每人配置自己的本机 ip 进行开发,每次开启开发环境的都需要修改,并且还 ...
- python pip安装找不到指定包的时候怎么解决
在该网址上下载对应版本的包然后安装即可. https://www.lfd.uci.edu/~gohlke/pythonlibs/
- ERP行业内幕看了这五个问题全懂了
ERP系统是现代企业实现信息化管理的必经之路.但很多管理人员或已经在用ERP的人员,其实并不太懂ERP系统是什么意思,有哪些好处等,导致实际使用过程中经常大材小用,或者“英雄无用武之地”.所以,为了更 ...
- javap指令
栈和局部变量操作将常量压入栈的指令aconst_null 将null对象引用压入栈iconst_m1 将int类型常量-1压入栈iconst_0 将int类型常量0压入栈iconst_1 将int类型 ...
- ubuntun与qt下载地址
http://mirrors.melbourne.co.uk/ubuntu-releases/ http://download.qt.io/archive/qt/5.4/5.4.0/ 使用u盘安装ub ...
- C#开发轻松入门--笔记
第一章 1-1 .NET简介 (02:11) 1-2 Visual Studio简介及安装 (03:23) 1-3 创建C#控制台程序 (04:14) 1-4 练习题 1-5 程序界面各部分介绍 (0 ...
- Row_Number() over()
分页 ROW_NUMBER() OVER (order by ID) 是先把ID列排序,再为排序以后的每条ID记录返回一个序号.
- oracle的用户账号密码设置
1. 可以用sqlplus system/你输入的密码 可以用sqlplus /nolog 可以用sqlplus /as sysdba2. @你scott.sql的路径3. 修改你的账号 alter ...