JLOI/SHOI2016黑暗前的幻想乡


\(n17\)

SOL:

采用容斥,至多n-1个公司-至多n-2个公司+至多n-3个公司……

用二进制数枚举每个公司是否选取,然后用矩阵树定理求行列式算出方案

时间复杂度\(O(2^nn^3)\)

注意数组大小

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
#define ll long long
const int N=20,M=1<<17,mod=1e9+7;
inline int ksm(int x,int r){
	int ret=1;
	for(int i=0;(1<<i)<=r;i++){
		if((r>>i)&1)ret=(ll)ret*x%mod;
		x=(ll)x*x%mod;
	}
	return ret;
}
int n,S,ans,m[N],a[N][N],u[N][504],v[N][504],sz[M];
inline int det(){
	int ret=1,fl=0;
	for(int i=1;i<n;i++){
		if(!a[i][i]){
			for(int j=i+1;j<n;j++)
				if(a[j][i]){
					swap(a[i],a[j]);fl^=1;
					break;
				}
		}
		if(!a[i][i])return 0;
		for(int j=i+1,inv=ksm(a[i][i],mod-2),tmp;j<n;j++){
			if(!a[j][i])continue;
			tmp=(ll)a[j][i]*inv%mod;
			for(int k=i;k<n;k++)a[j][k]=(a[j][k]-(ll)a[i][k]*tmp%mod+mod)%mod;
		}
	}
	for(int i=1;i<n;i++)ret=(ll)ret*a[i][i]%mod;
	return fl?(mod-ret)%mod:ret;
}
int main(){
	n=read();S=(1<<n-1)-1;
	for(int i=1;i<n;i++){
		m[i]=read();
		for(int j=1;j<=m[i];j++){
			u[i][j]=read();v[i][j]=read();
		}
	}
	for(int s=1,uu,vv;s<=S;s++){
		memset(a,0,sizeof(a));
		for(int i=1;i<n;i++){
			if(!((s>>i-1)&1))continue;
			for(int j=1;j<=m[i];j++){
				uu=u[i][j];vv=v[i][j];
				a[uu][uu]++;a[vv][vv]++;
				a[uu][vv]=(a[uu][vv]+mod-1)%mod;a[vv][uu]=(a[vv][uu]+mod-1)%mod;
			}
		}
		sz[s]=sz[s>>1]+(s&1);
		ans=((ll)ans+mod+(((n-sz[s])%2)?det():-det()))%mod;
	}
	cout<<ans;
	return (0-0);
}
posted @ 2020-03-21 21:52  starusc  阅读(130)  评论(0)    收藏  举报