2018年湖北省大学程序设计竞赛 D. Who killed Cock Robin(树形DP)

题意:求无向树有多少个不同的子树。单点也算。

这道题比赛的时候公式对了,但是最后求和求错了,迷,也是思路没有完全理清楚。

树形dp,核心思想是合并子树。

由于无向,我们随意确定一个点为根,便确定了树的结构。

dp[i]表示以节点i为根的,包含节点i的子树总数。

用dfs从根往下搜,回溯的时候合并子树统计方法总数。

显然叶子结点v,dp[v]=1;

然后回溯到其父节点u时,我们把dp[v]与dp[u]合并。

然后方程就有了: dp[u]=dp[u]+dp[u]*dp[v];

表示新的以u为根的子树的方法总数=原来以u为根的子树的方法总数+原来以u为根的子树的方法总数加上边(u,v)后增加的方法总数。(自己画一下图不难验证)

最后加起来就可以了。不要忘了mod。。。

代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mo=1e7+7;
const int maxn=200010;
int a[maxn],c[maxn],us[maxn];
ll dp[maxn],flag;
ll tmp,ans,cnt;
int n,m,x,y,z;
char s[maxn];
vector<int>vc[maxn];
void add(int x,int y)
{
    vc[x].push_back(y);
}
void dfs(int x,int fa)
{
    dp[x]=1;
    for(int i=0;i<vc[x].size();i++)
    {
        int v=vc[x][i];
        if(v!=fa){
            dfs(v,x);
           dp[x]=(dp[x]+dp[x]*dp[v])%mo;
        }
    }
    return ;
}
int main(){
    int T,cas;
    while(cin>>n)
    {
        int m=n-1;
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++) vc[i].clear();
        while(m--){
            cin>>x>>y;
            add(x,y);
            add(y,x);
        }
        ans=0;
        dfs(1,-1);
        for(int i=1;i<=n;i++) ans+=dp[i];
        cout<<ans%mo<<endl;
        //if(flag) puts("Yes");else puts("No");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lsd20164388/article/details/80047001