BZOJ#4596. [Shoi2016]黑暗前的幻想乡
4596: [Shoi2016]黑暗前的幻想乡
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 679 Solved: 398
Description
四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖
怪涌入了幻想乡,扰乱了幻想乡昔日的秩序。但是幻想乡的建制派妖怪(人类)
博丽灵梦和八云紫等人整日高谈所有妖怪平等,幻想乡多元化等等,对于幻想乡
目前面临的种种大问题却给不出合适的解决方案。
风间幽香是幻想乡里少有的意识到了问题的严重性的大妖怪。她这次勇敢的
站了出来参加幻想乡大选。提出包括在幻想乡边境建墙(并让人类出钱),大力
开展基础设施建设挽回失业率等一系列方案,成为了大选年出人意料的黑马并顺
利的当上了幻想乡的大统领。
幽香上台以后,第一项措施就是要修建幻想乡的公路。幻想乡有 N 个城市,
之间原来没有任何路。幽香向选民承诺要减税,所以她打算只修 N- 1 条路将
这些城市连接起来。但是幻想乡有正好 N- 1 个建筑公司,每个建筑公司都想
在修路的过程中获得一些好处。
虽然这些建筑公司在选举前没有给幽香钱,幽香还是打算和他们搞好关系,
因为她还指望他们帮她建墙。所以她打算让每个建筑公司都负责一条路来修。
每个建筑公司都告诉了幽香自己有能力负责修建的路是哪些城市之间的。所
以幽香打算选择 N-1 条能够连接幻想乡所有城市的边,然后每条边都交给一
个能够负责该边的建筑公司修建,并且每个建筑公司都恰好修一条边。
幽香现在想要知道一共有多少种可能的方案呢?两个方案不同当且仅当它
们要么修的边的集合不同,要么边的分配方式不同。
Input
第一行包含一个正整数 N(N<=17), 表示城市个数。
接下来 N-1 行,其中第 i行表示第 i个建筑公司可以修建的路的列表:
以一个非负数mi 开头,表示其可以修建 mi 条路,接下来有mi 对数,
每对数表示一条边的两个端点。其中不会出现重复的边,也不会出现自环。
Output
仅一行一个整数,表示所有可能的方案数对 10^9 + 7 取模的结果。
Sample Input
4
2 3 2 4 2
5 2 1 3 1 3 2 4 1 4 3
4 2 1 3 2 4 1 4 2
2 3 2 4 2
5 2 1 3 1 3 2 4 1 4 3
4 2 1 3 2 4 1 4 2
Sample Output
17
Problem:
每个人可以建某些点之间的边,求生成树满足n-1条边由n-1个人建成,就是每个人建一天边的生成树个数
Solution:
我们可以直接用Matrix-Tree定理求出没有限制的情况下的生成树个数
考虑怎么满足限制条件
直接考虑不太好像
我们考虑容斥
ans=总情况-不满足的情况
ans=总情况-至少有一个人没有建边+至少有两个人没有建边-~~
可以容易知到其正确性
所以我们二进制枚举一下,每次都用Matrix-Tree算一下就好了
附上代码:
#include<bits/stdc++.h> using namespace std; const int N=405,M=25; const int mod=1e9+7; struct node{int x,y;}t[M][N]; long long C[M][M];int m[M]; long long Guass(int n) { long long ans=1; for(int i=1;i<=n;i++) { for(int j=i+1;j<=n;j++) { long long a=C[i][i],b=C[j][i]; while(b) { long long temp=a/b;a%=b;swap(a,b); for(int k=i;k<=n;k++) C[i][k]=(C[i][k]-temp*C[j][k]%mod+mod)%mod,swap(C[i][k],C[j][k]); ans=-ans; } } if(!C[i][i]) return 0; ans=ans*C[i][i]%mod; } ans=(ans%mod+mod)%mod; return ans; } int main() { freopen("a.in","r",stdin); int n;scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d",&m[i]); for(int j=1;j<=m[i];j++) scanf("%d%d",&t[i][j].x,&t[i][j].y); } long long ans=0; for(int i=0;i<(1<<n-1);i++) { memset(C,0,sizeof(C)); int num=0; for(int j=1;j<n;j++) { if(!(i&(1<<j-1))) continue; num++; for(int k=1;k<=m[j];k++) { int a=t[j][k].x,b=t[j][k].y; C[a][b]--;C[b][a]--; C[a][a]++;C[b][b]++; } } if((n-1-num)%2) ans-=Guass(n-1); else ans+=Guass(n-1); ans=(ans%mod+mod)%mod; } printf("%lld\n",ans); return 0; }