Wannafly #1 Treepath(树形DP)

本文介绍了一种使用树形动态规划方法解决特定图论问题的算法:在一棵树中寻找长度为偶数的所有路径数量。通过定义两种类型的DP状态(路径长度为奇数和偶数的情况)来递归地解决问题,并给出完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

problem

给定一棵n个点的树,问其中有多少条长度为偶数的路径。路径的长度为经过的边的条数。x到y与y到x被视为同一条路径。路径的起点与终点不能相同。

Input

第一行一个数n表示点的个数;
接下来n-1行,每行两个整数x,y表示边;
保证输入数据形成一棵树;
1<=n<=100000

Output

一行一个整数表示答案。

Sample Input

3
1 2
1 3

Sample Output

1


思路

考虑tree DP,dp1【】数组表示以当前结点为根的子树中所有到根的路径长度为奇数的结点数
dp2【】数组表示以当前结点为根的子树中所有到根的路径长度为偶数的结点数。就有“父奇为儿偶,父偶为儿奇”
进入每个结点时,初始化为dp1[rt]=0,dp2[rt]=1;
就是说,“零距离”记为1个,这样在计数时,比如叶子节点的父节点,奇数就加上偶数(0+1=1),偶数就加上奇数(0+0=0),符合。

另外,最关键的一点在于 ans+=dp1[rt]*dp2[tt]+dp2[rt]*dp1[tt];
就是说,对当前这个根节点,有一个它的子树到他的奇  另外一个子树的奇(奇+奇=偶)(或者说是已经合并的,实际上这就是一个合并信息到根的过程),和偶 偶(偶+偶=偶),就是ans要加的值。


核心代码为

void dfs(int rt,int f)
{
    dp1[rt]=0;
    dp2[rt]=1;
    for(int i=head[rt];i!=-1;i=edges[i].to){
        int tt=edges[i].from;
        if(tt==f) continue;
        dfs(tt,rt);
        ans+=dp1[rt]*dp2[tt]+dp2[rt]*dp1[tt];
        dp1[rt]+=dp2[tt];//父奇为儿偶
        dp2[rt]+=dp1[tt];//父偶为儿奇
    }
}


代码示例

#include<iostream>
#include<string.h>
#include<cstdio>
using namespace std;

const int maxn=100010;
int head[maxn<<1];
int tot,n;//tot辅助存边(边集数组转邻接表),n为结点数

struct Edge{
    int from,to;
}edges[maxn<<1];

void init()
{
    memset(head,-1,sizeof(head));
    tot=0;
}

void add_edge(int u,int v){
    edges[tot].from=v;
    edges[tot].to=head[u];
    head[u]=tot++;
}

int dp1[maxn],dp2[maxn];
//1为奇路径方案数,2为偶路径方案数

long long ans=0;//答案

void dfs(int rt,int f)
{
    dp1[rt]=0;
    dp2[rt]=1;
    for(int i=head[rt];i!=-1;i=edges[i].to){
        int tt=edges[i].from;
        if(tt==f) continue;
        dfs(tt,rt);
        ans+=dp1[rt]*dp2[tt]+dp2[rt]*dp1[tt];
        dp1[rt]+=dp2[tt];
        dp2[rt]+=dp1[tt];
    }
}

int main()
{
    //ios::sync_with_stdio(false);
        int u,v;
        scanf("%d",&n);
        init();
        for(int i=1;i<n;++i){
            scanf("%d %d",&u,&v);
            add_edge(u,v);
            add_edge(v,u);
        }
        dfs(1,-1);
        cout<<ans<<endl;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值