bzoj 4596
4596: [Shoi2016]黑暗前的幻想鄉
Time Limit:?20 Sec??Memory Limit:?256 MBSubmit:?257??Solved:?152
[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
42 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 詳細地講一下 不要問為什么,這道題用容斥原理+矩陣樹定理 1.矩陣樹定理可以求一個單位權圖的生成樹的個數,這個東西可以自己上網找一下。 2.容斥原理: 首先我們想一下,題目要求每個公司選一條自己能建的邊,那么很好啊,我們把每個人的邊都加進去,跑矩陣樹定理,不就出來了嗎?當然不了,因為可能出現某個人的邊沒被選。這很討厭,那么我們就要減去這些情況,這些情況都包含什么情況呢?假如一號公司的邊沒被選中,那么這些情況是要減掉的,不如我們把一號公司的邊全刪掉,再跑矩陣樹定理,加上,然后把二號公司的邊都刪去,然后。。。是不是就刪去了所有那些有人沒選的情況?不,這樣刪多了,因為我們去掉一號公司的邊,跑矩陣樹定理,也把二號公司沒選的方案減了,跑二號公司時,這些情況也減去了,就減多了,那么怎么加上這些情況呢?不如我們把一號公司二號公司的邊全刪掉,再跑。。。一直這樣,這就是容斥原理。。。 2^n枚舉一個狀態,一個位是一表示這個公司的所有邊都加進去了,如果公司%2的數量和總數%2相等,就加上,否則減去。 記住數組要開大,邊數有17*16/2種。 #include<bits/stdc++.h> using namespace std; typedef long long ll; #define N 30 #define mod 1000000007 struct edge {int u[1010],v[1010],size; }x[N]; int n; ll ans; ll a[N][N],d[N][N],g[N][N]; ll gauss() {ll f=1,ret=1;for(int i=1;i<=n;++i){for(int j=i+1;j<=n;++j){ll A=g[i][i],B=g[j][i];while(B){ll t=A/B; A%=B; swap(A,B);for(int k=i;k<=n;++k) g[i][k]=((g[i][k]-t*g[j][k]%mod)%mod+mod)%mod;for(int k=i;k<=n;++k) swap(g[i][k],g[j][k]);f=-f;}} if(!a[i][i]) return 0;}for(int i=1;i<=n;++i) ret=ret*g[i][i]%mod;ret=(ret*f%mod+mod)%mod;return ret; } void solve() {for(int i=1;i<1<<n;++i){memset(a,0,sizeof(a));memset(g,0,sizeof(g));memset(d,0,sizeof(d));int tot=0;for(int j=1;j<=n;++j) if(i&(1<<(j-1))){++tot;for(int k=1;k<=x[j].size;++k){ int u=x[j].u[k],v=x[j].v[k];a[u][u]++; a[v][v]++;d[u][v]++; d[v][u]++;}}for(int j=1;j<=n;++j)for(int k=1;k<=n;++k) g[j][k]=a[j][k]-d[j][k];if(n%2==tot%2) ans+=gauss();else ans-=gauss();ans=(ans%mod+mod)%mod;}ans=(ans%mod+mod)%mod;printf("%lld\n",ans); } int main() {scanf("%d",&n);--n;for(int i=1;i<=n;++i){scanf("%d",&x[i].size);for(int j=1;j<=x[i].size;++j) scanf("%d%d",&x[i].u[j],&x[i].v[j]);} // if(!n) // { // puts("1"); // return 0; // } solve();return 0; } View Code?
轉載于:https://www.cnblogs.com/19992147orz/p/6410981.html
總結
- 上一篇: C#设计模式--简单工厂模式
- 下一篇: ARC下带CF前缀的类型与OC类型转换