[BZOJ4596][SHOI2016]黑暗前的幻想乡

bzoj
luogu

description

一张无向图上有\(n-1\)中颜色的边,求包含每种颜色恰好一条边的生成树的个数模\(10^9+7\)
\(n\le17\)

sol

先大力猜一波这题的复杂度是\(O(2^n\times n^3)\)
\(2^{n-1}\)枚举每种颜色是否可以选,然后算出用可以选的颜色的边任意构造生成树的方案,矩阵树可以直接做。接着容斥一下就行了。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 20;
const int mod = 1e9+7;
int n,len[N],u[N][N*N],v[N][N*N],chos[N],a[N][N],ans;
int work(){
	memset(a,0,sizeof(a));int res=1;
	for (int i=1;i<n;++i)
		if (chos[i])
			for (int j=1;j<=len[i];++j)
				++a[u[i][j]][u[i][j]],++a[v[i][j]][v[i][j]],--a[u[i][j]][v[i][j]],--a[v[i][j]][u[i][j]];
	for (int i=1;i<=n;++i)
		for (int j=1;j<=n;++j)
			a[i][j]=(a[i][j]+mod)%mod;
	for (int i=2;i<=n;++i){
		for (int j=i+1;j<=n;++j)
			while (a[j][i]){
				int t=a[i][i]/a[j][i];
				for (int k=i;k<=n;++k)
					a[i][k]=(a[i][k]-1ll*t*a[j][k]%mod+mod)%mod,swap(a[i][k],a[j][k]);
				res=(mod-res)%mod;
			}
		res=1ll*res*a[i][i]%mod;
	}
	return res;
}
void dfs(int u,int op){
	if (u==n){
		ans+=1ll*op*work()%mod;if (ans>=mod) ans-=mod;
		return;
	}
	chos[u]=1;dfs(u+1,mod-op);
	chos[u]=0;dfs(u+1,op);
}
int main(){
	n=gi();
	for (int i=1;i<n;++i){
		len[i]=gi();
		for (int j=1;j<=len[i];++j)
			u[i][j]=gi(),v[i][j]=gi();
	}
	dfs(1,n&1?1:mod-1);
	printf("%d\n",ans);
	return 0;
}
posted @ 2018-06-29 22:01  租酥雨  阅读(232)  评论(0编辑  收藏  举报