hdu5647 DZY Loves Connecting (树形dp)

中档的树形dp题

问题描述 DZY有一棵nnn个结点的无根树,结点按照1∼n1\sim n1∼n标号。

DZY喜欢树上的连通集。一个连通集SSS是由一些结点组成的集合,满足SSS中任意两个结点u,vu,vu,v能够用树上的路径连通,且路径上不经过SSS之外的结点。显然,单独一个结点的集合也是连通集。

一个连通集的大小定义为它包含的结点个数,DZY想知道所有连通集的大小之和是多少。你能帮他数一数吗?

答案可能很大,请对109+710^9 + 710​9​​+7取模后输出。

输入描述 第一行ttt,表示有ttt组数据。 接下来ttt组数据。每组数据第111行一个数nnn。第2∼n2\sim n2∼n行中,第iii行包含一个数pip_ip​i​​,表示iii与pip_ip​i​​有边相连。(1≤pi≤i−1,2≤i≤n1\le p_i \le i-1,2\le i\le n1≤p​i​​≤i−1,2≤i≤n)

(n≥1n\ge 1n≥1,所有数据中的nnn之和不超过200000200000200000)

输出描述 每组数据输出一行答案,对109+710^9 + 710​9​​+7取模。

输入样例 2 1 5 1 2 2 3

输出样例 1 42

题解: rootnum[i]表示以i为根节点的连通集的大小和; sum[i]表示以i为根节点的连通集的个数; 对节点x而言对每个子节点flag进行以下操作 rootnum[x]=(rootnum[x](sum[flag]+1))%mod+sum[x]rootnum[flag]%mod; sum[x]=sum[x](sum[flag]+1)%mod; 其中rootnum[x](sum[flag]+1)表示每个新加入的子节点的连通集个数(还有空集)数与原本连通集(x及之前x子节点形成的连通集)进行组合,表示当新子节点加入时对原本连通集大小的增幅(或者说贡献); 其中sum[x]rootnum[flag]表示新加入的连通集(新加入的子节点及其子节点的连通集)与原本连通集的个数进行组合,,表示当新子节点加入时原本连通集个数对新加入子节点的连通集大小的贡献; 其中sum[x]=sum[x](sum[flag]+1)更新了以i为根节点且包含i的连通集个数;

#include<cstdio>
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
vector<int>ve[200005];
long long all;
const int mod=1e9+7;
long long  sum[200005],rootnum[200005];
int dfs(int x,int father)
{
    rootnum[x]=1,sum[x]=1;
    for(int i=0;i<ve[x].size();i++)
    {
        int flag=ve[x][i];
        if(flag==father)
        {
            continue;
        }
        dfs(flag,x);
        rootnum[x]=(rootnum[x]*(sum[flag]+1))%mod+sum[x]*rootnum[flag]%mod;
        sum[x]=sum[x]*(sum[flag]+1)%mod;
    }
    all=(all+rootnum[x])%mod;
}
int main()
{
    int t,n,read;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        all=0;
        for(int i=1;i<=n;i++)
        {
            ve[i].clear();
        }
        for(int i=2;i<=n;i++)
        {
            scanf("%d",&read);
            ve[i].push_back(read);
            ve[read].push_back(i);
        }
        dfs(1,0);
        printf("%I64d\n",all);
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值