suxxsfe

一言(ヒトコト)

P4336 [SHOI2016]黑暗前的幻想乡

https://www.luogu.com.cn/problem/P4336

如果没有每个公司分别修一条路的要求,那么可以直接把每个公司能修的路分别加到图中,然后跑矩阵树
加上这个要求,可以看作求有 \(0\) 个公司不修路的方案数,于是容斥,对于每个 \(m \le n-1\) 求出有 \(m\) 个公司不修路的方案数(枚举是哪 \(m\) 个公司),然后根据容斥原理分别乘上 \(1\)\(-1\) 即可

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
#include<map>
#define reg register
#define LL_INF (long long)(0x3f3f3f3f3f3f3f3f)
#define INT_INF (int)(0x3f3f3f3f)
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
#define mod 1000000007
#define N 25
int n;
int u[N][N*N],v[N][N*N];
long long a[N][N];
inline long long det(){
	long long ans=1;
	for(reg int i=1;i<=n;i++){
		for(reg int j=i+1;j<=n;j++){
			if(a[j][i]>a[i][i]) std::swap(a[j],a[i]),ans=-ans;
			while(a[j][i]){
				long long k=a[i][i]/a[j][i];
				for(reg int h=1;h<=n;h++) a[i][h]=(a[i][h]+(mod-k)*a[j][h])%mod;
				std::swap(a[i],a[j]);
				ans=-ans;
			}
		}
	}
	for(reg int i=1;i<=n;i++) ans=ans*a[i][i]%mod;
	return (ans+mod)%mod;
}
int main(){
	n=read();
	for(reg int i=1;i<n;i++){
		u[i][0]=read();
		for(reg int j=1;j<=u[i][0];j++) u[i][j]=read(),v[i][j]=read();
	}
	int lim=1<<(n-1);
	long long ans=0;
	for(reg int S=0;S<lim;S++){
		int cnt=0;
		std::memset(a,0,sizeof a);
		for(reg int i=1;i<n;i++)if(S&(1<<(i-1))){
			cnt++;
			for(reg int j=1;j<=u[i][0];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(reg int i=1;i<=n;i++)for(reg int j=1;j<=n;j++) a[i][j]=(mod+a[i][j])%mod;
		cnt=n-cnt;cnt&=1;
		n--;
		ans=(ans+mod+(cnt?det():(-det())))%mod;
		n++;
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2021-03-10 19:46  suxxsfe  阅读(38)  评论(0编辑  收藏  举报