bzoj4596 [Shoi2016]黑暗前的幻想乡

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

Sample Output

17

 

正解:容斥+矩阵树定理。

很裸的一道题(我也不知道我为什么要写这道题。。

直接对于$n-1$个公司容斥,用总方案减去少了一个公司的方案再加上少了两个公司的方案...

然后用矩阵树定理求出所有方案并乘上容斥系数即可,复杂度$O(2^{n-1}*n^{3})$。

 

 1 #include <bits/stdc++.h>
 2 #define il inline
 3 #define RG register
 4 #define ll long long
 5 #define rhl (1000000007)
 6 
 7 using namespace std;
 8 
 9 struct edge{ int u,v; }e[20][205];
10 
11 int a[20][20],len[20],n,all,ans;
12 
13 il int gi(){
14   RG int x=0,q=1; RG char ch=getchar();
15   while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
16   if (ch=='-') q=-1,ch=getchar();
17   while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
18   return q*x;
19 }
20 
21 il int qpow(RG int a,RG int b){
22   RG int ans=1;
23   while (b){
24     if (b&1) ans=1LL*ans*a%rhl;
25     if (b>>=1) a=1LL*a*a%rhl;
26   }
27   return ans;
28 }
29 
30 il int gauss(){
31   RG int res=1;
32   for (RG int i=1,id,inv;i<n;++i){
33     for (id=i;id<n;++id) if (a[id][i]) break;
34     if (id<n && id!=i){
35       for (RG int j=i;j<n;++j) swap(a[i][j],a[id][j]);
36       res=-res;
37     }
38     if (!a[i][i]) return 0; inv=qpow(a[i][i],rhl-2);
39     for (RG int j=i+1,tmp;j<n;++j){
40       if (!a[j][i]) continue; tmp=1LL*inv*a[j][i]%rhl;
41       for (RG int k=i;k<n;++k)
42     a[j][k]=(a[j][k]-1LL*a[i][k]*tmp)%rhl;
43     }
44     res=1LL*res*a[i][i]%rhl;
45   }
46   return (res+rhl)%rhl;
47 }
48 
49 int main(){
50 #ifndef ONLINE_JUDGE
51   freopen("dark.in","r",stdin);
52   freopen("dark.out","w",stdout);
53 #endif
54   n=gi(),all=1<<(n-1);
55   for (RG int i=0;i<n-1;++i){
56     len[i]=gi();
57     for (RG int j=0;j<len[i];++j)
58       e[i][j].u=gi(),e[i][j].v=gi();
59   }
60   for (RG int S=0,cnt=0;S<all;++S,cnt=0){
61     memset(a,0,sizeof(a));
62     for (RG int i=0;i<n-1;++i)
63       if (S>>i&1) ++cnt; else{
64     for (RG int j=0,x,y;j<len[i];++j){
65       x=e[i][j].u,y=e[i][j].v;
66       ++a[x][x],++a[y][y];
67       --a[x][y],--a[y][x];
68     }
69       }
70     ans+=(cnt&1)?(rhl-gauss()):gauss();
71     if (ans>=rhl) ans-=rhl;
72   }
73   cout<<ans; return 0;
74 }

 

posted @ 2018-02-27 08:49  wfj_2048  阅读(217)  评论(0编辑  收藏  举报