P4336 [SHOI2016]黑暗前的幻想乡
答案=没有限制的方案数 - (限制1公司没修的方案 ∪ 限制2公司没修的方案 ∪ 限制3公司没修的方案 ∪...∪ 限制n-1公司没修的方案)
矩阵树计算方案数,然后容斥
根据容斥原理
(限制1公司没修的方案 ∪ 限制2公司没修的方案 ∪ 限制3公司没修的方案 ∪...∪ 限制n-1公司没修的方案)
=(所有限制一个公司没修的方案-所有限制两个公司没修的方案+所有限制三个公司没修的方案-所有限制四个公司没修的方案....)
那么枚举数的二进制表示每个公司的参加情况然后计算贡献
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<vector> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int mo=1e9+7; int n; ll G[27][27]; struct edge{ int x,y; }; vector <edge> V[27]; int Gauss() { ll ans=1; for(int i=1;i<n;i++) { for(int j=i+1;j<n;j++) while(G[j][i]) { ll t=G[i][i]/G[j][i]; for(int k=i;k<=n;k++) G[i][k]=(G[i][k] - t*G[j][k])%mo; swap(G[i],G[j]); ans=-ans; } ans=ans*G[i][i]%mo; if(!ans) return 0; } return (ans+mo)%mo; } int main() { n=read(); int a,x,y; for(int i=1;i<n;i++) { a=read(); for(int j=1;j<=a;j++) { x=read(),y=read(); V[i].push_back((edge){x,y}); } } int ans=0,mx=(1<<n-1)-1; for(int i=0;i<=mx;i++) { int cnt=0; memset(G,0,sizeof(G)); for(int j=1;j<n;j++) if((i>>j-1)&1) { for(int k=V[j].size()-1;k>=0;k--) { x=V[j][k].x,y=V[j][k].y; G[x][y]--; G[y][x]--; G[x][x]++; G[y][y]++; } cnt++; } if((n-cnt)&1) ans=(ans+Gauss())%mo; else ans=(ans-Gauss()+mo)%mo; } printf("%d",ans); return 0; }