bzoj4596 [Shoi2016]黑暗前的幻想乡
题目描述:
题解:
容斥+矩阵树。
考虑用其中$i$个人(没有每人一条路的限制)能组成的生成树的个数。
所以$2^{n-1}$枚举然后$n^3$高消即可。
高消常数小不虚。
代码:
#include<vector> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N = 20; const int MOD = 1000000007; template<typename T> inline void read(T&x) { T f = 1,c = 0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();} x = f*c; } template<typename T> inline void Mod(T&x){if(x>=MOD)x-=MOD;} int fastpow(int x,int y) { int ret = 1; while(y) { if(y&1)ret=1ll*ret*x%MOD; x=1ll*x*x%MOD;y>>=1; } return ret; } int inv(int x){return fastpow(x,MOD-2);} struct Pair { int u,v; Pair(){} Pair(int u,int v):u(u),v(v){} }; int n,a[N][N]; vector<Pair>ve[N]; int gs() { int ret = 1; for(int i=1;i<n;i++) { int tmp = i; while(tmp<n&&!a[tmp][i])tmp++; if(tmp==n)return 0; if(tmp!=i) { ret = MOD-ret; for(int j=i;j<n;j++) swap(a[i][j],a[tmp][j]); } ret = 1ll*ret*a[i][i]%MOD; int now = inv(a[i][i]); for(int j=i;j<n;j++) a[i][j] = 1ll*a[i][j]*now%MOD; for(int j=i+1;j<n;j++)if(a[j][i]) { now = a[j][i]; for(int k = i;k<n;k++) Mod(a[j][k] += MOD-1ll*now*a[i][k]%MOD); } } return ret; } int main() { read(n); for(int m,u,v,i=0;i<n-1;i++) { read(m); while(m--) read(u),read(v),ve[i].push_back(Pair(u,v)); } int ans = 0; for(int i=0;i<(1<<(n-1));i++) { int f = (n&1?1:-1);memset(a,0,sizeof(a)); for(int j=0;j<n-1;j++)if(i&(1<<j)) { f = -f; for(int k=0,lim=ve[j].size();k<lim;k++) { int u = ve[j][k].u,v = ve[j][k].v; a[u][u]++,a[v][v]++; Mod(a[u][v]+=MOD-1),Mod(a[v][u]+=MOD-1); } } int now = gs(); Mod(ans += (f==1?now:MOD-now)); } printf("%d\n",ans); return 0; }