码迷,mamicode.com
首页 > 其他好文 > 详细

[ZJOI2016]小星星

时间:2019-01-11 12:11:10      阅读:182      评论:0      收藏:0      [点我收藏+]

标签:ios   很多   eof   并且   amp   复杂度   ring   代码   sizeof   

动态规划$+$容斥原理:

1、暴力状压

我们尝试设计状态:$$f[i][j][S]$$

表示新的图上点$i$对应旧的图上点$j$并且所有点的状态为一个二进制数$S$时的方案数

但是这样做为什么说是暴力呢。。。一看就知道,复杂度爆炸,然而我并不会证$qwq$

2、正解(可能吧):容斥原理

我们思考上面这种方法为什么会爆炸,就是因为有了最后那维状态,如果我们可以省去那个状态的话,就可以降低时间复杂度了,于是我们设:

$$f[i][j]$$表示新的图上点$i$对应旧的图上点$j$时的方案数

那么我们就面临一个问题,就是有很多重复的状态我们都算进来了,那么我们就考虑用容斥原理就好啦

我们发现只要集合长度:$|S|=n$时的方案数减去$|S|=n-1$时的方案数加上$|S|=n-2$时的方案数减去$|S|=n-3$时的方案数$......$

一加一减就阔以了

代码:

#include<iostream>
#include<cstdio>
#include<cstring> 
#define ll long long
#define N 19
#define M 140
using namespace std;
struct Edge
{
	int to,nxt;
}edge[N<<1];
int n,m,cnt;
ll ans;
int ban[N],head[N],g[N][N];
ll f[N][N];
void Add(int u,int v)
{
	edge[++cnt]=(Edge){v,head[u]};
	head[u]=cnt;
}
void Dfs(int u,int fa)
{
	for(int i=1;i<=n;++i)
		f[u][i]=1;
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v==fa)
			continue;
		Dfs(v,u);
		for(int j=1;j<=n;++j)
		{
			ll sum=0;
			for(int k=1;k<=n;++k)
				sum+=f[v][k]*(g[k][j]&&ban[j]&&ban[k]);
			f[u][j]*=sum;
		}
	}
}
signed main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		g[u][v]=g[v][u]=1;
	}
	for(int i=1;i<n;++i)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		Add(u,v);
		Add(v,u);
	}
	for(int i=0;i<(1<<n);++i)
	{
		memset(ban,0,sizeof(ban));
		ll ret=0;
		int size=n,now=i;
		for(int j=1;now;now>>=1,++j)
			ban[j]=(now&1),size-=(now&1);
	//	for(int j=1;j<=n;++j)
	//		printf("%d ",ban[j]);
	//	printf("\n");
		Dfs(1,0);
		for(int j=1;j<=n;++j)
			ret+=f[1][j];
		if(size%2)
			ans-=ret;//printf("%d\n",ans);
		else
			ans+=ret;
	}
	printf("%lld",ans);
	return 0;
}

  

[ZJOI2016]小星星

标签:ios   很多   eof   并且   amp   复杂度   ring   代码   sizeof   

原文地址:https://www.cnblogs.com/yexinqwq/p/10254401.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!