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);
}
posted @ 2019-01-06 18:22  fcwww  阅读(142)  评论(0编辑  收藏  举报