SHOI2016 黑暗前的幻想乡

题目链接:戳我

幻想乡是个什么东西??(逃

矩阵树定理+容斥

就是设\(dp[i]\)表示至多i个公司修建道路,那么我们有\(ans=dp[n-1]-dp[n-2]+dp[n-3]......\)balabala(就是那个容斥公式嘛qwqwq)

然后每次都跑一次矩阵树定理qwqwq

但是这样的时间复杂度是\(O(n^32^{n-1})\)???所以说。。。跑的。。。2s。。。很勉强qwqwq

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define MAXN 100010
#define mod 1000000007
using namespace std;
int n,maxx,ans;
int cnt[MAXN],dp[20][20];
struct Node{int x,y;};
vector<Node>node[20];
inline int getnum(int x)
{
	int cur_ans=0;
	for(int i=0;i<=21;i++)
		if(x&(1<<i))
			cur_ans++;
	return cur_ans;
}
inline int matrix_tree()
{
	int cur_ans=1;
	for(int i=2;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			while(dp[j][i])
			{
				int t=dp[i][i]/dp[j][i];
				for(int k=i;k<=n;k++)
					dp[i][k]=(dp[i][k]-1ll*dp[j][k]*t%mod+mod)%mod;
				swap(dp[i],dp[j]);
				cur_ans=(-cur_ans+mod)%mod;
			}
		}
		cur_ans=(1ll*cur_ans*dp[i][i])%mod;
	}
	return cur_ans;
}
inline int calc(int x)
{
	memset(dp,0,sizeof(dp));
	for(int i=1;i<n;i++)
	{
		if(x&(1<<(i-1)))
		{
			for(int j=0;j<node[i].size();j++)
			{
				int u=node[i][j].x,v=node[i][j].y;
				dp[u][u]=(dp[u][u]+1)%mod;
				dp[v][v]=(dp[v][v]+1)%mod;
				dp[u][v]=(dp[u][v]-1+mod)%mod;
				dp[v][u]=(dp[v][u]-1+mod)%mod;
			}
		}
	}
	return matrix_tree();
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("ce.in","r",stdin);
	freopen("ce.out","w",stdout);
	#endif
	scanf("%d",&n);
	maxx=(1<<n-1)-1;
	for(int i=1;i<n;i++)
	{
		int k,u,v;
		scanf("%d",&k);
		for(int j=1;j<=k;j++)
		{
			scanf("%d%d",&u,&v);
			node[i].push_back((Node){u,v});
		}
	}
	for(int i=0;i<=maxx;i++) cnt[i]=getnum(i);
	for(int i=0;i<1<<(n-1);++i) 
		ans=(ans+(((n-1-cnt[i])&1)?mod-calc(i):calc(i)))%mod;
	printf("%d\n",ans);
	return 0;
}
posted @ 2019-02-25 11:04  风浔凌  阅读(129)  评论(0编辑  收藏  举报