Through Path(树上差分)

题目链接:

E - Through PathAtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.https://atcoder.jp/contests/abc187/tasks/abc187_e

题目大意:

给定一个树,每条边连接a和b,每一条边可能有两种操作:

1.以a为起点并且不经过b的所有点权值+x

2.以b为起点并且不经过a的所有点权值+x

输出最终所有点的权值

学习笔记:

这道题主要用了树上差分的方法,它实际上是把前缀和思想引入图问题的一种方法。首先我们知道,前缀和必须是在有顺序的情况下用的,而无根树本身是没有顺序的,所以必须先把无根数转换成有根树结构。有根树是从父节点遍历到子结点的,这样就有了顺序。

再看到这道题,a和b是相连的,假如说a是b的父节点。假如要让b部分(以b为起点,不经过a的所有点)加x,那么就让val[b] += x即可,假如要让a部分(以a为起点,不经过b的所有点)加x,可以在全局范围内取b部分的补集,因为除了b部分就是a部分,我们让val[1] += x,val[b] -= x就完成了取反。(直接寻找a部分比较困难,就想到了取补集的方法)

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <iomanip>
#include <string.h>
#include <climits>
using namespace std;
#define int long long
const int maxn = 2e5+5;
vector<int> g[maxn]; // g[i]用来存和i相连的所有点
int val[maxn]; // 存储所有点的val
int dep[maxn]; // 存储所有点的高度
int a[maxn]; // 存储边的左边顶点
int b[maxn]; // 存储边的右边顶点
int n, q; // 顶点数,操作次数

// 求x结点的孩子的高度,记到dep中,fa是x的父节点
void getDepth(int x, int fa){
    for(int v : g[x]){
        if(v == fa) continue; //跳过父节点
        dep[v] = dep[x] + 1;
        getDepth(v, x); // 继续遍历计算孩子的孩子的高度
    }
}

// 汇总所有和,计算最终结果
void getAns(int x, int fa){
    for(int v : g[x]){
        if(v == fa) continue;
        val[v] += val[x];
        getAns(v, x);
    }
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    cin >> n;
    for(int i=1; i<n; i++){ //这里需要特别注意,n个点,n-1条边
        // 输入并储存边
        cin >> a[i] >> b[i];
        g[a[i]].push_back(b[i]);
        g[b[i]].push_back(a[i]);
    }
    getDepth(1, 1); // 1结点的高度是0,不用算,这里是计算它的所有孩子的高度
    cin >> q;
    while(q--){
        int t, e, x; // 操作的点的编号,边的编号,要加的值
        cin >> t >> e >> x;
        int node1 = a[e], node2 = b[e];
        if (t == 1) { // 经过a不经过b,对应node1
            if(dep[node1] < dep[node2]){
                // a部分加x,看成全部加x,然后b部分减x
                val[node2] -= x;
                val[1] += x; // 1结点加上x,相当于全局加x
            } else {
                val[node1] += x;
            }
        }
        else if(t == 2){
            if(dep[node2] < dep[node1]){
                val[node1] -= x;
                val[1] += x;
            } else {
                val[node2] += x;
            }
        }
    }

    getAns(1, 1); // 从1结点开始,计算所有结果
    for(int i=1; i<=n; i++){
        cout <<  val[i] << endl;
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值