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
 
和ZJOI小星星很像啊。
考慮容斥原理,設A(x)表示x公司參與修建的方案集合,那麼答案等價於求|A(1)∩A(2)∩……∩A(n)|,容斥轉化成並集形式,而後用MatrixTree定理計算便可。
實際複雜度爲O(N^3*2^N)。
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
    if(head==tail) {
        int l=fread(buffer,1,BufferSize,stdin);
        tail=(head=buffer)+l;
    }
    return *head++;
}
inline int read() {
    int x=0,f=1;char c=Getchar();
    for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
    return x*f;
}
typedef long long ll;
const int maxn=20;
const int mod=1000000007;
typedef ll Matrix[maxn][maxn];
void gcd(ll a,ll b,ll& x,ll& y) {
    if(!b) x=1,y=0;
    else gcd(b,a%b,y,x),y-=x*(a/b);
}
ll getinv(ll a) {
    ll b=mod,x,y;gcd(a,b,x,y);
    return (x+mod)%mod;
}
Matrix B;
ll gauss(Matrix A,int n) {
    ll ans=1;
    rep(i,0,n-1) {
        int r=i;
        rep(j,i+1,n-1) if(abs(A[r][i])<abs(A[j][i])) r=j;
        if(r!=i) ans*=-1,swap(A[r],A[i]);
        ll inv=getinv(A[i][i]);
        rep(k,0,n-1) if(i!=k) 
           dwn(j,n-1,i) A[k][j]=(A[k][j]-A[k][i]*A[i][j]%mod*inv%mod+mod)%mod;
    }
    rep(i,0,n-1) (ans*=A[i][i])%=mod;
    return (ans+mod)%mod;
}
struct Company {
    int m,u[maxn*maxn],v[maxn*maxn];
}A[maxn];
int main() {
    int n=read();
    rep(i,0,n-2) dwn(j,A[i].m=read(),1) A[i].u[j]=read(),A[i].v[j]=read();
    ll ans=0;
    rep(S,0,(1<<n-1)-1) {
        int cnt=0;
        rep(i,0,n-1) rep(j,0,n-1) B[i][j]=0;
        rep(i,0,n-2) if(S>>i&1) {
            cnt++;
            rep(j,1,A[i].m) {
                int u=A[i].u[j]-1,v=A[i].v[j]-1;
                B[u][u]++;B[v][v]++;B[u][v]--;B[v][u]--;
            }
        }
        rep(i,0,n-1) rep(j,0,n-1) (B[i][j]+=mod)%=mod;
        if(n-1-cnt&1) (ans-=gauss(B,n-1))%=mod;
        else (ans+=gauss(B,n-1))%=mod;
    }
    printf("%lld\n",(ans+mod)%mod);
    return 0;
}
相關文章
相關標籤/搜索