首页 > 其他分享 >[ZJOI2016] 小星星

[ZJOI2016] 小星星

时间:2023-01-23 22:22:15浏览次数:68  
标签:int 小星星 细线 饰品 ZJOI2016 对应 dp

[ZJOI2016]小星星

题目描述

小 Y 是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有 \(n\) 颗小星星,用 \(m\) 条彩色的细线串了起来,每条细线连着两颗小星星。

有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了 \(n-1\) 条细线,但通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小 Y 找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。小 Y 想知道有多少种可能的对应方式。

只有你告诉了她正确的答案,她才会把小饰品做为礼物送给你呢。

输入格式

第一行包含 \(2\) 个正整数 \(n,m\),表示原来的饰品中小星星的个数和细线的条数。

接下来 \(m\) 行,每行包含 \(2\) 个正整数 \(u,v\),表示原来的饰品中小星星 \(u\) 和 \(v\) 通过细线连了起来。这里的小星星从 \(1\) 开始标号。保证 \(u\neq v\),且每对小星星之间最多只有一条细线相连。

接下来 \(n-1\) 行,每行包含 \(2\) 个正整数 \(u,v\),表示现在的饰品中小星星 \(u\) 和 \(v\) 通过细线连了起来。保证这些小星星通过细线可以串在一起。

输出格式

输出共 \(1\) 行,包含一个整数表示可能的对应方式的数量。

如果不存在可行的对应方式则输出 0

样例 #1

样例输入 #1

4 3
1 2
1 3
1 4
4 1
4 2
4 3

样例输出 #1

6

提示

对于 \(100\%\) 的数据,\(n\leq 17\),\(m\leq \frac 12n(n-1)\)。

这题到现在已经变成了模板题了。

如果这题想要直接dp,就肯定逃不开状压。在一棵树上,跑一个状压背包,看起来还要用FWT优化。是可以这样做,不过有一种简单很多的方法。

我们为什么要状压?无非就是为了让所有的点都用到。让所有的点都用到,想到了容斥。

在 dp 的外面枚举在图中用到的点集 \(S\),然后定义 \(dp_{i,j}\) 表示如果点 \(i\) 对应图中点 \(j\) 的方案数。枚举另一个树上的点在图上对应哪一个点,并保证图上的点只用 \(S\) 中的点,跑出来 dp 值乘上 \((-1)^{|S|}\) 累积到答案上就行了。

#include<bits/stdc++.h>
typedef long long LL;
const int N=20;
int n,m,hd[N],e_num,g[N][N],cnt;
LL dp[N][N],ans,ret,s;
struct edge{
	int v,nxt;
}e[N<<1];
void add_edge(int u,int v)
{
	e[++e_num]=(edge){v,hd[u]} ;
	hd[u]=e_num;
}
void dfs(int x,int y,int z)
{
//	printf("%d %d %d\n",x,y,z);
	for(int i=hd[x];i;i=e[i].nxt)
		if(e[i].v!=z)
			dfs(e[i].v,y,x);
	for(int i=0;i<n;i++)
	{
		dp[x][i]=0;
		if(!(y>>i&1))
			continue;
		dp[x][i]=1;
		for(int k=hd[x];k;k=e[k].nxt)
		{
			if(e[k].v==z)
				continue;
			s=0;
			for(int j=0;j<n;j++)
				if(g[i+1][j+1]&&(y>>j&1))
					s+=dp[e[k].v][j];
			dp[x][i]*=s;
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1,u,v;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		g[u][v]=g[v][u]=1;
	}
	for(int i=1,u,v;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		add_edge(u,v);
		add_edge(v,u);
	}
	for(int i=1;i<(1<<n);i++)
	{
		cnt=0,ans=0;
		for(int j=0;j<n;j++)
			if(i>>j&1)
				++cnt;
		dfs(1,i,0);
		for(int j=0;j<n;j++)
			ans+=dp[1][j];
		ret+=(n-cnt&1)? -ans:ans;
//		printf("%d %d\n",i,ans);
	}
	printf("%lld ",ret);
}

标签:int,小星星,细线,饰品,ZJOI2016,对应,dp
From: https://www.cnblogs.com/mekoszc/p/17065620.html

相关文章

  • [dp 记录]P3349 [ZJOI2016]小星星
    绝世容斥好题,刚好NOIp前要复习容斥,就拉过来当100紫了。祝自己明天的NOIprp++这题好久前看过题解,感觉好可惜,浪费了好题。以后自己不会的题也不能看题解了。题意:......