2019.02.09 bzoj4455: [Zjoi2016]小星星(容斥原理+dp)

本文探讨了在图论中,如何使用容斥原理解决特定的图与树的匹配问题,通过枚举和动态规划的方法,计算在限制条件下树与图的同构方案数。

传送门
题意简述:给一张图和一棵树(点数都为n≤17n \le17n17),问有多少种给树的标号方法方法使得图中去掉多余的边之后和树一模一样。


思路:
容斥好题啊。
考虑fi,jf_{i,j}fi,j表示把iii对应成原图中的点jjj这棵子树的对应方案数。
然后转移就枚举儿子看能不能转,如果可以就更新当前答案。
但是这样会有多个树中的节点对应到同一个图中的节点上。
于是我们用2n2^n2n的时间去枚举可以对应的原图的点集合然后容斥即可。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int N=18;
typedef long long ll;
int n,m,tot,a[N];
ll f[N][N],ans=0,sum;
bool trans[N][N];
vector<int>e[N];
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
inline void dfs(int p,int fa){
	for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])^fa)dfs(v,p);
	for(ri i=1;i<=tot;++i){
		f[p][i]=1;
		for(ri j=0,v;j<e[p].size();++j){
			if((v=e[p][j])==fa)continue;
			ll tmp=0;
			for(ri k=1;k<=tot;++k)if(trans[a[i]][a[k]])tmp+=f[v][k];
			f[p][i]*=tmp;
		}
	}
}
int main(){
	n=read(),m=read();
	for(ri i=1,u,v;i<=m;++i)u=read(),v=read(),trans[u][v]=trans[v][u]=1;
	for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
	for(ri i=1;i<(1<<n);++i){
		tot=0,sum=0;
		for(ri j=1;j<=n;++j)if((i>>(j-1))&1)a[++tot]=j;
		dfs(1,0);
		for(ri j=1;j<=tot;++j)sum+=f[1][j];
		ans+=sum*((n-tot)&1?-1:1);
	}
	cout<<ans;
	return 0;
}

转载于:https://www.cnblogs.com/ldxcaicai/p/10367699.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值