bzoj 4596
考虑容斥,容斥系数-1
首先不难发现,如果没有一个公司一条边这个限制的话,就是一个很简单的矩阵树定理了
可是有了这个限制,就会出现重复
因此我们用容斥原理来解决
我们枚举哪个(些)公司没被用到,对剩下的公司跑矩阵树定理,乘一个容斥系数累计贡献即可
时间复杂度$O(n^{3}2^{n})$
代码:
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> #include <vector> #define ll long long using namespace std; const ll mode=1000000007; ll C[25][25],D[25][25],A[25][25]; struct node { int l,r; node (){} node (int a,int b):l(a),r(b){} }V[25][505]; int num[25]; int n; ll pow_mul(ll x,ll y) { ll ret=1; while(y) { if(y&1)ret=ret*x%mode; x=x*x%mode,y>>=1; } return ret; } ll get_inv(ll x) { return pow_mul(x,mode-2); } ll Gauss() { int N=n-1; ll ans=1,f=1; for(int i=1;i<=N;i++) { int temp=i; while(!C[temp][i]&&temp<=N)temp++; if(temp!=i){f=-f;for(int j=i;j<=N;j++)swap(C[i][j],C[temp][j]);} ll inv=get_inv(C[i][i]); for(int j=i+1;j<=N;j++) { ll now=C[j][i]; for(int k=i;k<=N;k++)C[j][k]=(C[j][k]-inv*now%mode*C[i][k]%mode+mode)%mode; } if(!C[i][i])return 0; ans=ans*C[i][i]%mode; } return (ans*f+mode)%mode; } int main() { scanf("%d",&n); for(int i=1;i<n;i++) { int m; scanf("%d",&m); num[i]=m; for(int j=1;j<=m;j++) { int st,ed; scanf("%d%d",&st,&ed); D[st][st]++,D[ed][ed]++; A[st][ed]++,A[ed][st]++; V[i][j]=node(st,ed); } } ll S=0; for(int i=0;i<(1<<(n-1));i++) { ll f=1; for(int j=0;j<n-1;j++) { if((1<<j)&i) { f=-f; for(int k=1;k<=num[j+1];k++)D[V[j+1][k].l][V[j+1][k].l]--,D[V[j+1][k].r][V[j+1][k].r]--,A[V[j+1][k].l][V[j+1][k].r]--,A[V[j+1][k].r][V[j+1][k].l]--; } } for(int j=1;j<n;j++)for(int k=1;k<n;k++)C[j][k]=(D[j][k]-A[j][k]+mode)%mode; S=(S+f*Gauss()+mode)%mode; for(int j=0;j<(n-1);j++)if((1<<j)&i)for(int k=1;k<=num[j+1];k++)D[V[j+1][k].l][V[j+1][k].l]++,D[V[j+1][k].r][V[j+1][k].r]++,A[V[j+1][k].l][V[j+1][k].r]++,A[V[j+1][k].r][V[j+1][k].l]++; } printf("%lld\n",S); return 0; }