bzoj 4596: [Shoi2016]黑暗前的幻想乡【容斥原理+矩阵树定理】

真是简单粗暴
把矩阵树定理的运算当成黑箱好了反正我不会
这样我们就可以在O(n^3)的时间内算出一个无向图的生成树个数了
然后题目要求每个工程队选一条路,这里可以考虑容斥原理:全选的方案数-不选工程队1能修的路的方案数-不选工程队2能修的路的方案数……+不选工程队12能修的路的方案数+不选工程队13能修的路的方案数……-不选工程队123能修的路的方案数……
这里直接O(2^(n-1))枚举选择状态即可,然后根据不选的个数奇偶来决定在ans上减或加即可

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int N=20,mod=1e9+7;
int n,a[N][N];
long long ans;
vector<pair<int,int> >v[N];
int read()
{
	int r=0,f=1;
	char p=getchar();
	while(p>'9'||p<'0')
	{
		if(p=='-')
			f=-1;
		p=getchar();
	}
	while(p>='0'&&p<='9')
	{
		r=r*10+p-48;
		p=getchar();
	}
	return r*f;
}
int gaosi(int n)
{
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(a[i][j]<0)
				a[i][j]+=mod;
	long long ans=1,f=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			long long x=a[i][i],y=a[j][i];
			while(y)
			{
				long long t=x/y;
				x%=y;
				swap(x,y);
				for(int k=i;k<=n;k++)
					a[i][k]=(a[i][k]-a[j][k]*t%mod+mod)%mod;
				for(int k=i;k<=n;k++)
					swap(a[i][k],a[j][k]);
				f=-f;
			}
		}
		if(a[i][i]==0)
			return 0;
		ans=ans*a[i][i]%mod;
	}
	return f==-1?(mod-ans)%mod:ans;
}
int main()
{
	n=read();
	for(int i=1;i<n;i++)
	{
		int m=read();
		for(int j=1;j<=m;j++)
		{
			int x=read(),y=read();
			v[i].push_back(make_pair(x,y));
		}
	}
	for(int i=0;i<(1<<(n-1));i++)
	{
		int s=n-1,x=i;
		memset(a,0,sizeof(a));
		for(int j=1;j<n;j++,x>>=1)
			if(x&1)
			{
				s--;
				for(int k=0;k<v[j].size();k++)
					a[v[j][k].first][v[j][k].first]++,a[v[j][k].second][v[j][k].second]++,a[v[j][k].first][v[j][k].second]--,a[v[j][k].second][v[j][k].first]--;
			}
		if(s&1)
			ans=(ans-gaosi(n-1))%mod;
		else
			ans=(ans+gaosi(n-1))%mod;
	}
	printf("%d
",(ans+mod)%mod);
	return 0;
}
原文地址:https://www.cnblogs.com/lokiii/p/9253168.html