BZOJ 4596: [Shoi2016]黑暗前的幻想乡 矩阵树定理+容斥
求生成树方案的话要用矩阵树定理,然后这个容斥就是常见套路了吧.
code:
#include <cstring> #include <cstdio> #include <vector> #include <algorithm> #define N 18 #define mod 1000000007 #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; int a[N][N],n,det[N][N][N],bu[N]; int qpow(int x,int y) { int tmp=1; for(;y;y>>=1,x=(ll)x*x%mod) if(y&1) tmp=(ll)tmp*x%mod; return tmp; } int INV(int x) { return qpow(x,mod-2); } int gauss() { int ans=1,i,j,k; for(i=2;i<=n;++i) { k=i; for(j=i+1;j<=n;++j) if(a[j][i]>a[k][i]) k=j; if(k!=i) swap(a[i],a[k]),ans*=-1; if(!a[i][i]) return 0; int inv=INV(a[i][i]); for(j=i+1;j<=n;++j) { int t=(ll)((ll)inv*a[j][i]%mod+mod)%mod; for(k=i;k<=n;++k) a[j][k]=(ll)(a[j][k]%mod-(ll)t*a[i][k]%mod+mod)%mod; } } if(ans<0) ans+=mod; for(i=2;i<=n;++i) ans=(ll)((ll)ans*a[i][i]%mod+mod)%mod; return ans; } // 度数-邻接矩阵 void build(int x) { int i,j; for(i=1;i<=n;++i) for(j=1;j<=n;++j) a[i][j]+=det[x][i][j]; } int main() { // setIO("input"); int i,j,m; scanf("%d",&n); for(i=1;i<=n-1;++i) { scanf("%d",&m); for(j=1;j<=m;++j) { int x,y; scanf("%d%d",&x,&y); det[i][x][x]++; det[i][y][y]++; det[i][x][y]--; det[i][y][x]--; } } for(i=0;i<N;++i) bu[i]=1<<i; int ans=0; for(i=bu[n-1]-1;i>0;--i) { memset(a,0,sizeof(a)); int siz=0; for(j=0;bu[j]<=i;++j) if(bu[j]&i) build(j+1),++siz; int tmp=gauss(); // printf("%d\n",tmp); int d=((n-1-siz)%2==0)?1:mod-1; ans=(ll)(ans+(ll)d*tmp%mod+mod)%mod; } printf("%d\n",ans); return 0; }