F. Leaf Partition 题解

F. Leaf Partition题目来源()

题目大意:给你一颗n个节点的树(根结点为1),要你将叶子节点分成若干个集合,然后设f(x)表示将集合x中所有的叶子结点连成最小的连通图子图,求有多少种划分方法使得所有的f(x)互不相交。

样例:

5

1 1 1 1

输出

12

 

正解:这里考虑用树形DP。

设 f[x]表示以x为根结点,并且不会再向上走去联通其他点的方案数,g[x]表示以x为根结点,要向上去联通其他点的方案数。

我们可以发现 f[x]=g[x]=Π(v∈son[x])(f[v]+g[v]); 但里面有很多方案数是不可能的 例如 求f[x]时所有的儿子中只有一个向上延伸的。(毕竟有向上延伸的所以必须存在另一个与其联通,所以不能再f[x]这里结束。)

所有f[x]就等于总方案数-不合法的方案数===(设sum=Π(v∈son[x])f[v]),(v∈son[x])f[x]-=sum/f[v]*g[v];

g[x]同理不能存在一个向上延伸都没有的 所以g[x]=g[x]-sum;

注意要取模,所以逆元 long long什么的

答案 就为f[1];

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int i,j,n,m,k,l,e[300001][3],h[300001],tot,x;
long long g[300001],f[300001],mo;
void add(int u,int v)
{
	e[++tot][0]=h[u];
	e[tot][1]=v;
	h[u]=tot;
}
long long ksm(long long x,long long y)
{
	long long res=1;
	while(y)
	{
		if(y%2==1) res*=x,res%=mo;
		x*=x;x%=mo;y/=2;
	}
	return res;
}
void dfs(int x)
{
	if(h[x]==0){f[x]=g[x]=1;return;}
	long long sum=1;f[x]=g[x]=1;
	for(int i=h[x];i;i=e[i][0])
	{
		dfs(e[i][1]);
		sum*=f[e[i][1]],sum%=mo;
		f[x]*=(g[e[i][1]]+f[e[i][1]]);f[x]%=mo;
		g[x]*=(g[e[i][1]]+f[e[i][1]]);g[x]%=mo;
	}
	for(int i=h[x];i;i=e[i][0])
	{
		f[x]-=(sum*ksm(f[e[i][1]],mo-2))%mo*g[e[i][1]]%mo;
		if(f[x]<0) f[x]+=mo; 
	} 
	g[x]-=sum;if(g[x]<0)g[x]+=mo;
}
int main()
{
	scanf("%d",&n);mo=998244353;
	for(i=2;i<=n;i++)
		scanf("%d",&x),add(x,i);
	dfs(1);
	printf("%lld",f[1]);
}

 

  

作者不易,,,ε=(´ο`*)))唉,,,

转载请附上链接Thanks♪(・ω・)ノ

posted @ 2020-10-15 20:47  *LZX*  阅读(126)  评论(0编辑  收藏  举报