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♪(・ω・)ノ