1. 程式人生 > >[SHOI2016]黑暗前的幻想鄉

[SHOI2016]黑暗前的幻想鄉

等等 script 過程 二進制 names 最大 個數 目前 重復

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 這題直接Matrix-Tree肯定是不行的,因為我們要把n-1條邊分給n-1個不同公司
直接計算出來的方案包括有些公司未參與的修建方案 令f[i]為禁止i個公司參與的方案數,利用容斥原理 $ans=f[0]-f[1]+f[2]-f[3]....(-1)^{n}*f[n]$ 可以枚舉二進制狀態,參加為1,禁止參加為0 然後建出相應的基爾霍夫矩陣,根據Matrix-Tree定理算出行列式的值 根據0的數量決定加減
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6
using namespace std; 7 struct ZYYS 8 { 9 int u,v; 10 }edge[18][501]; 11 int n,a[18][18],Mod=1e9+7,len[18],ans; 12 int guass() 13 {int i,j,k,s; 14 n--; 15 for (i=1;i<=n;i++) 16 { 17 for (j=1;j<=n;j++) 18 { 19 a[i][j]=(a[i][j]+Mod); 20 if (a[i][j]>=Mod) a[i][j]-=Mod; 21 } 22 } 23 s=1; 24 for (i=1;i<=n;i++) 25 { 26 for (j=i+1;j<=n;j++) 27 while (a[j][i]) 28 { 29 int t=a[i][i]/a[j][i]; 30 for (k=i;k<=n;k++) 31 { 32 a[i][k]=(a[i][k]-1ll*t*a[j][k]%Mod+Mod); 33 if (a[i][k]>=Mod) a[i][k]-=Mod; 34 swap(a[i][k],a[j][k]); 35 } 36 s*=-1; 37 } 38 s=1ll*s*a[i][i]%Mod; 39 if (!s) 40 { 41 n++; 42 return 0; 43 } 44 } 45 n++; 46 return (s+Mod)%Mod; 47 } 48 int main() 49 {int i,j,k,as,cnt; 50 cin>>n; 51 for (i=1;i<=n-1;i++) 52 { 53 scanf("%d",&len[i]); 54 for (j=1;j<=len[i];j++) 55 { 56 scanf("%d%d",&edge[i][j].u,&edge[i][j].v); 57 } 58 } 59 for (i=0;i<=(1<<n-1)-1;i++) 60 { 61 cnt=0; 62 memset(a,0,sizeof(a)); 63 for (j=1;j<=n-1;j++) 64 if ((i&(1<<j-1))) 65 { 66 for (k=1;k<=len[j];k++) 67 { 68 int x=edge[j][k].u,y=edge[j][k].v; 69 a[x][y]--;a[y][x]--; 70 a[x][x]++;a[y][y]++; 71 } 72 } 73 else cnt++; 74 as=guass(); 75 if (cnt%2) 76 ans=(ans-as+Mod); 77 else ans=(ans+as); 78 if (ans>=Mod) ans-=Mod; 79 } 80 cout<<(ans+Mod)%Mod; 81 }

[SHOI2016]黑暗前的幻想鄉