您的位置:首页 > 其它

HDU5647 DZY Loves Connecting 树形DP

2016-03-20 16:46 246 查看
(先奉上jcvb大神的官方题解)BC 76 div 1 1002

对于每个结点i,统计出f[i]表示包含i的连通集有多少个,那么容易看出答案就是所有f[i]的和。

要计算f[i]是经典的树形DP问题。令g[i]表示以i为根的连通集的数目,那么g[i]=∏(g[j]+1),

j是i的儿子,按这个从下往上DP一遍。

然后再用同样的式子从上往下DP一遍就好了。这一步骤写的时候要注意一下不要写错。

时间复杂度O(n)。

然后本蒟蒻不会这么写,因为一些人说需要逆元,而且这个题卡逆元,所以果断没有写

然后果断看了rank1的代码,然后写了一发,A掉了

蒟蒻大体说一下rank1的思路,看了半天请教学长才会

分析:题意是无根树,然后把它转化成有根树,这样每一个联通集都有一个跟节点

下面来定义g[i]代表以 i 为根节点的联通集的方案数,

f[i]代表以 i 为根节点的每个方案的大小之和

然后可见 ans就是所有f[i]的数量和

注:(这里的g[i]与官方题解一样,f[i]是不一样的)

代码:

#include <stdio.h>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long LL;
const int N=2e5+5;
const LL mod=1e9+7;
LL g
,f
,ans;
int T,n,head
,tot;
struct Edge{
int v,next;
}edge[N<<1];
void add(int u,int v){
edge[tot].v=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void dfs(int u,int fa){
f[u]=g[u]=1;
for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].v;
if(v==fa)continue;
dfs(v,u);//处理子节点
f[u]=(f[u]*(g[v]+1)%mod+g[u]*f[v]%mod)%mod;
//处理到当前子节点分支,如何得到当前f[u]
//进行分治,讨论连通集是否包含这个新分支中的点
//这就是上面的式子,我就是分治理解的
g[u]=g[u]*(g[v]+1)%mod;
//得到当前子节点的以u为根的方案数
}
ans=(ans+f[u])%mod;
}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d",&n);
ans=tot=0;
for(int i=1;i<=n;++i)head[i]=-1;
for(int i=2;i<=n;++i){
int v;
scanf("%d",&v);
add(i,v),add(v,i);
}
dfs(1,0);
printf("%I64d\n",ans);
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: