题解:luogu P3349([ZJOI2016] 小星星)

1. Description

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

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

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

2. Solution

首先考虑一个暴力的树形 dp,\(f_{u,i,st}\) 表示 \(u\) 代表点 \(i\)\(u\) 的子树代表的点集为 \(st\) 的方案数,转移平凡,时间复杂度是 \(O(n^33^n)\),显然很难通过。
问题主要出在 \(st\) 这一维上,我们考虑通过容斥压缩掉这一维。
考虑,如果只定义 \(f_{u,i}\),则可能会出现若干个图中的点在树上没有与之对应的点,因此,我们钦定一个点集 \(st\),当我们做 dp 的时候,只允许树上的点对应 \(st\) 中的点,定义当限定的点集为 \(st\) 的时候的答案为 \(g_{st}\),则最后的答案就是 \(\sum_{st} (-1)^{n-|st|}g_{st}\),单次 dp 的时间复杂度显然是 \(O(n^3)\) 的,则最后的时间复杂度为 \(O(n^32^n)\)

3. Code

/*by ChenMuJiu*/
/*略去缺省源与快读快写*/
int n,m,tot;
ll ans;
int cnt[1<<17],idx[20];
ll f[20][20];
bool flag[20][20];
vector<int>e[20];
void dfs(int u,int fa){
	for(int i=0;i<tot;i++)
		f[u][i]=1;
	for(int v:e[u]){
		if(v==fa)continue;
		dfs(v,u);
		for(int i=0;i<tot;i++){
			ll sum=0;
			for(int j=0;j<tot;j++)
				if(flag[idx[i]][idx[j]])
					sum+=f[v][j];
			f[u][i]*=sum;
		}
	}
}
signed main(){
	read(n),read(m);
	for(int i=1,u,v;i<=m;i++){
		read(u),read(v);
		flag[u][v]=flag[v][u]=1;
	}
	for(int i=2,u,v;i<=n;i++){
		read(u),read(v);
		e[u].push_back(v);
		e[v].push_back(u);
	}
	for(int st=0;st<(1<<n);st++){
		cnt[st]=cnt[st>>1]+(st&1);
		tot=0;
		for(int j=0;j<n;j++)
			if((st>>j)&1){
				idx[tot++]=j+1;
			}
		dfs(1,0);
		for(int j=0;j<tot;j++)
			ans+=((n-tot)&1?-f[1][j]:f[1][j]);
	}
	write(ans),Nxt;
}
posted @ 2026-05-23 21:08  陈牧九  阅读(14)  评论(0)    收藏  举报