p4336 [SHOI2016]黑暗前的幻想乡
分析
对于所有边建Kirchhoff矩阵求出生成树个数
我们发现可能存在有多条边是一个人建得的情况
于是我们考虑容斥
因为数据范围很小所以我们可以枚举所有的不同人的集合
对于每个集合求出方案数
然后容斥即可
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
const int mod = 1e9+7;
int x[20][400],y[20][400],g[20][20],m[20],n;
inline int gs(){
int i,j,k,ans=1;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
g[i][j]=(g[i][j]%mod+mod)%mod;
for(i=1;i<=n;i++){
for(j=i;j<=n;j++)
if(g[j][i])break;
if(j>n)return 0;
if(j!=i)ans=mod-ans,swap(g[i],g[j]);
for(j=i+1;j<=n;j++){
while(g[j][i]){
int t=g[i][i]/g[j][i];
for(k=i;k<=n;k++)
g[i][k]=(g[i][k]-1ll*g[j][k]*t%mod+mod)%mod;
swap(g[i],g[j]);
ans=mod-ans;
}
}
ans=1ll*ans*g[i][i]%mod;
}
return ans;
}
int main(){
int i,j,k,t,ans=0;
scanf("%d",&n);
for(i=1;i<n;i++){
scanf("%d",&m[i]);
for(j=1;j<=m[i];j++)
scanf("%d%d",&x[i][j],&y[i][j]);
}
for(i=1;i<(1<<(n-1));i++){
memset(g,0,sizeof(g));
t=0;
for(j=1;j<n;j++)
if((1<<(j-1))&i){
t++;
for(k=1;k<=m[j];k++){
int xx=x[j][k],yy=y[j][k];
g[xx][xx]++;
g[yy][yy]++;
g[xx][yy]--;
g[yy][xx]--;
}
}
n--;
int res=gs();
n++;
if((t&1)!=(n&1))ans=(ans+res)%mod;
else ans=(ans-res+mod)%mod;
}
printf("%d\n",ans);
return 0;
}