1. 程式人生 > >[luogu3244 SHOI2016] 黑暗前的幻想鄉(容斥原理+矩陣樹定理)

[luogu3244 SHOI2016] 黑暗前的幻想鄉(容斥原理+矩陣樹定理)

getc ems href 建築 != dig algorithm bool mem

傳送門

Description

給出 n 個點和 n?1 種顏色,每種顏色有若幹條邊。求這張圖多少棵每種顏色的邊都出現過的生成樹,答案對 109+7 取模。

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

Solution

隨意選的-一個顏色不選+兩個顏色不選。。。
暴力枚舉所有情況求出生成樹個數統計到答案中即可

Code

//By Menteur_Hxy
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define F(i,a,b) for(register int i=(a);i<=(b);i++)
using namespace std;
typedef long long LL;

int read() {
    int x=0,f=1; char c=getchar();
    while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
    while(isdigit(c)) x=(x<<1)+(x<<3)+c-48,c=getchar();
    return x*f;
}

const int MOD=1000000007;
bool vis[20];
int n,m[20];
int vx[20][400],vy[20][400];
LL ans,ma[20][20];

LL qpow(LL a,LL b) {
    LL t=1;
    while(b) {
        if(b&1) t=t*a%MOD;
        a=a*a%MOD; b>>=1;
    }
    return t;
}

void dfs(int x,int flag) {
    if(x==n) {
        memset(ma,0,sizeof(ma));
        LL now=1,ret;
        F(i,1,n-1) if(vis[i]) 
            F(j,1,m[i]) ma[vx[i][j]][vx[i][j]]++,ma[vy[i][j]][vy[i][j]]++,
                ma[vx[i][j]][vy[i][j]]--,ma[vy[i][j]][vx[i][j]]--;
        int i,j,k;
        for(i=2;i<=n;i++) {
            for(j=i;j<=n;j++) if(ma[j][i]) break; 
            if(j>n) break;
            if(j!=i) {
                flag=-flag;
                F(k,i,n) swap(ma[i][k],ma[j][k]);
            }
            now=now*ma[i][i]%MOD; ret=qpow(ma[i][i],MOD-2);
            for(j=i;j<=n;j++) ma[i][j]=ma[i][j]*ret%MOD;
            for(j=i+1;j<=n;j++) for(ret=ma[j][i],k=i;k<=n;k++)
                ma[j][k]=(ma[j][k]-ret*ma[i][k]%MOD+MOD)%MOD;
        }
        if(i>n) ans=(ans+flag*now+MOD)%MOD;
        return ;
    }
    vis[x]=1; dfs(x+1,flag);
    vis[x]=0; dfs(x+1,-flag);
}

int main() {
    n=read();
    F(i,1,n-1) {
        m[i]=read();
        F(j,1,m[i]) vx[i][j]=read(),vy[i][j]=read();
    }
    dfs(1,1);
    printf("%lld",ans);
    return 0;
}

[luogu3244 SHOI2016] 黑暗前的幻想鄉(容斥原理+矩陣樹定理)