树形DP+树上路径贡献

题目

一棵树有n个节点,每个节点都同有一个符号+或者-,表示该节点是正极节点还是负极节点。如果从正极走到负极,或者从负极走到正极,都会受到1点伤害。
现在需要知道走过所有路径后会受到的总伤害是多少?树上任意2点存在唯一的路径。需要计算所有任意2点的路径的伤害和。

注意:从u到,和从v到u视为同一条路径。

输入描述

第一行输入一个整数n(1≤n ≤ 10^5 )
第二行输入一个长度为1的仅由+和-组成的字符串。
接下来n-1 行,每行输入两个整数 u,v(1≤u,v≤n) 表示节点
u和节点v之间有一条边。

输出描述

输出一个整数表示受到的总伤害。

样例1

输入

3
++-
1 2
1 3

输出
2

说明
路径1-2 (2-1) 不会受到伤害。
脂径1-3(3-1) 会受到1点伤害。
路径2-1-3(3-1-2)会受到1点害。
因此答案为2。

题目分析

典型的树形DP,换根可以解决路径去重的问题。现在考虑怎么计算贡献。
我们可以很容易想到一个f[i]: i的子树上所有经过点i的路径的伤害值的和。那么1nf[i]就是答案

image
在DFS时到1的时候,考虑1-3的边,在考虑2和3的子树合并的时候计算f[1], 当前增加的路径是,根(1,2,4,5)到3点所有子树(3, 6)的路径。因为每条路径一定会经过1。 对每条路径进行拆分, (XY)=(X1)+(1Y)
X是(1,2,4,5),Y是(3, 6)。
每个X会和每个Y匹配出一条路径,那么对于X-1会被计算Y次。对于每个根u,我们求出它的字树到根u的路径和f1[u]=x=(xu)
那么这个公式
(XY)=(X1)+(1Y)
转化成
f[u]=f1[u]son[to]+f1[to]son[to]

f1[to]在计算前,应该处理u-to这条边的伤害。

代码如下:

#include <iostream>

using namespace std;

char ch[100005];
bool vis[100005];
vector<int> G[100005];
long long f[100005], f1[100005], son[100005];

void Dfs(int u, int fa){
    son[u] = 1;
    for (auto to : G[u]) {
        if (to != fa) {
            Dfs(to, u);
            // 处理边u-to的伤害
            if (ch[u] != ch[to]) {
                f1[to] += son[to];
            }
            // 计算贡献
            f[u] += f1[u] * son[to] + f1[to] * son[u];
            // 换根
            f1[u] += f1[to];
            son[u] += son[to];
        }
    }
}

int main() {
    
    int  n; scanf("%d", &n);
    scanf("%s", ch+1);
    for(int i=1; i<n; i++) {
        int a, b; scanf("%d%d", &a, &b);
        G[a].push_back(b);
        G[b].push_back(a);
    }
    long long ans = 0;
    Dfs(1, 0);
    for (int i=1; i<=n; i++) {
        ans += f[i];
    }
    printf("%lld\n", ans);
    
    return 0;
}
/*
7
++--+-+
1 2
2 3
2 4
3 5
1 6
6 7
 
5
+-++-
1 2
2 3
2 4
1 5
 
3
+--
 1 2
 1 3
 
 8
+-++-+-+
 1 2
 2 3
 3 4
 3 5
 1 6
 6 7
 6 8
 
*/
posted @   liweihang  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
Live2D
欢迎阅读『树形DP+树上路径贡献』
点击右上角即可分享
微信分享提示