BZOJ4596: [Shoi2016]黑暗前的幻想乡
BZOJ4596: [Shoi2016]黑暗前的幻想乡
https://lydsy.com/JudgeOnline/problem.php?id=4596
分析:
- 题中求每个建筑公司都恰好修一条边的方案数。
- 容斥一下,转化成求有偶数个公司不修的方案数减去奇数个公司不修的方案数。
- 矩阵树定理即可。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 20
#define mod 1000000007
typedef long long ll;
int n,siz[N],e[N][N*N],idx[N][N],is[N];
int ex[N*N],ey[N*N];
ll ans,a[N][N];
ll qp(ll x,ll y) {
ll re=1;
for(;y;y>>=1,x=x*x%mod) if(y&1) re=re*x%mod; return re;
}
ll Gauss(int n) {
int i,j,k,flg=0; ll re=1,del;
for(i=1;i<=n;i++) {
for(j=i;j<=n&&!a[j][i];j++) ;
if(j>n) continue;
if(i!=j) {
for(k=i;k<=n;k++) swap(a[i][k],a[j][k]);
flg^=1;
}
for(j=i+1;j<=n;j++) if(a[j][i]) {
del=a[j][i]*qp(a[i][i],mod-2)%mod;
for(k=i;k<=n;k++) a[j][k]=(a[j][k]-del*a[i][k])%mod;
}
}
for(i=1;i<=n;i++) re=re*a[i][i]%mod;
if(flg) re=-re;
return (re+mod)%mod;
}
ll work() {
int i,j;
memset(a,0,sizeof(a));
for(i=1;i<n;i++) if(is[i]) {
for(j=1;j<=siz[i];j++) {
int x=ex[e[i][j]], y=ey[e[i][j]];
a[x][x]++; a[y][y]++; a[x][y]--; a[y][x]--;
}
}
return Gauss(n-1);
}
void dfs(int dep,int s) {
if(dep==n) {
if((n-s-1)&1) ans=(ans-work())%mod;
else ans=(ans+work())%mod;
return ;
}
is[dep]=1;
dfs(dep+1,s+1);
is[dep]=0;
dfs(dep+1,s);
}
int main() {
scanf("%d",&n);
int i,j,x,y,tot=0;
for(i=1;i<=n;i++) for(j=i+1;j<=n;j++) idx[i][j]=++tot,ex[tot]=i,ey[tot]=j;
for(i=1;i<n;i++) {
scanf("%d",&siz[i]);
for(j=1;j<=siz[i];j++) {
scanf("%d%d",&x,&y);
if(x>y) swap(x,y);
e[i][j]=idx[x][y];
}
}
dfs(1,0);
printf("%lld\n",(ans+mod)%mod);
}