4596: [Shoi2016]黑暗前的幻想乡
考虑矩阵树定理,但是不能解决十分选择了n-1个公司,所以我们可以容斥,总方案数-一个公司不选+两个公司不选-…
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MOD=1e9+7;
int n,cnt[1<<17],Ans,f[20][20];
struct Edge{int x,y;};
struct xcw{
Edge que[200];int tot;
void read(){++tot,scanf("%d%d",&que[tot].x,&que[tot].y);}
}a[20];
int Gauss(){
int Now=1;
for(int i=1;i<n;i++){
for(int j=i+1;j<n;j++)
while(f[j][i]){
int t=f[i][i]/f[j][i];
for(int k=i;k<n;k++) f[i][k]=(f[i][k]-1ll*t*f[j][k]%MOD+MOD)%MOD;
swap(f[i],f[j]),Now=-Now;
}
Now=1ll*Now*f[i][i]%MOD;
}
return (Now+MOD)%MOD;
}
void Add(int x,int y){f[x][x]++,f[x][y]--,f[y][y]++,f[y][x]--;}
void Work(int x,int opt){
memset(f,0,sizeof(f));
for(int i=0;i<n-1;i++)
if((x>>i)&1) for(int j=1;j<=a[i].tot;j++) Add(a[i].que[j].x,a[i].que[j].y);
Ans=((Ans+opt*Gauss())%MOD+MOD)%MOD;
}
int main(){
scanf("%d",&n);
for(int i=0;i<n-1;i++){
int K;scanf("%d",&K);
for(int j=1;j<=K;j++) a[i].read();
}
for(int i=1;i<(1<<n-1);i++) cnt[i]=cnt[i>>1]+(i&1);
for(int i=1;i<(1<<n-1);i++) Work(i,((n-cnt[i])&1)?1:-1);
printf("%d\n",Ans);
return 0;
}