4596: [Shoi2016]黑暗前的幻想乡
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 92 Solved: 60
[Submit][Status][Discuss]
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
HINT
Source
题解:
容斥原理+矩阵树定理
首先,先用二进制去枚举所有情况,1代表当前这个公司的道路要添加,0代表不添加。例如 011 ,代表一二两个公司的道路要添加。然后对于011,加入一二两个公司的道路。但加入道路后,用矩阵树定理跑出来的是所有最小生成树的个数,也不一定会选到一号公司或二号公司。所以要把只选上一号公司或只选上二号公司的减去,然后两个公司都没有选的减重了,再加上都不选的即可。这个就是容斥原理。设道路数为n,公司数为n-1,把和n-1奇偶性相同的加上,不同的减去。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<map> #include<vector> #include<queue> using namespace std; #define LL long long #define MOD 1000000007 vector<int> U[20],V[20]; int m[20]; LL a[20][20],id[20]; int read() { int s=0,fh=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')fh=-1;ch=getchar();} while(ch>='0'&&ch<='9'){s=s*10+(ch-'0');ch=getchar();} return s*fh; } LL Gauss(LL a[20][20],int n) { int i,j,k; LL ret=1,t; for(i=1;i<=n;i++) { for(j=i+1;j<=n;j++) { while(a[j][i]!=0) { t=a[i][i]/a[j][i]; for(k=i;k<=n;k++)a[i][k]=(a[i][k]-a[j][k]*t)%MOD; for(k=i;k<=n;k++)swap(a[i][k],a[j][k]); ret=-ret; } } if(a[i][i]==0LL)return 0LL; ret=(ret*a[i][i])%MOD; } //if(ret<0LL)ret=-ret; return (ret%MOD+MOD)%MOD; } int main() { int n,i,j,tot,wz,k,M; LL ans=0LL,Get; n=read(); for(i=0;i<=n-2;i++) { M=read(); for(j=1;j<=M;j++){U[i].push_back(read());V[i].push_back(read());} } for(i=(1<<(n-1))-1;i>=0;i--) { tot=0; memset(a,0,sizeof(a)); memset(id,0,sizeof(id)); for(j=0;j<=n-2;j++) { wz=((i>>j)&1); if(wz==1) { for(k=0;k<=U[j].size()-1;k++) { a[U[j][k]][V[j][k]]--;a[V[j][k]][U[j][k]]--; id[U[j][k]]++;id[V[j][k]]++; } for(k=1;k<=n;k++)a[k][k]=id[k]; tot++; } } Get=Gauss(a,n-1); if(((n-1)-tot)%2==0)ans=(ans+Get)%MOD; else ans=(ans-Get)%MOD; } printf("%lld",(ans%MOD+MOD)%MOD); fclose(stdin); fclose(stdout); return 0; }