题目链接

题目

题目描述

已知有 \(n\) 个节点,有 \(n-1\) 条边,形成一个树的结构。

给定一个根节点 \(k\) ,每个节点都有一个权值,节点i的权值为 \(v_i\) 。

给 \(m\) 个操作,操作有两种类型:

1 a x :表示将节点 \(a\) 的权值加上 \(x\)

2 a :表示求 \(a\) 节点的子树上所有节点的和(包括 \(a\) 节点本身)

输入描述

第一行给出三个正整数 \(n,m,k\) ,表示树的节点数、操作次数、和这棵树的根节点.第二行给出 \(n\) 个正整数,第 iii 个正整数表示第 \(i\) 个节点的权值 \(val_i\)下面 \(n-1\) 行每行两个正整数 \(u,v\) ,表示边的两个端点接下来 \(m\) 行,每行给出一个操作

输出描述

对于每个类型为 2 的操作,输出一行一个正整数,表示以 \(a\) 为根的子树的所有节点的权值和

示例1

输入

5 6 1
1 2 3 4 5
1 3
1 2
2 4
2 5
1 2 10
1 3 10
1 4 5
1 5 1
2 3
2 2

输出

13
27

备注

\(1\leq n,m\leq 1e6,1\leq k\leq n\)

\(1\leq u,v \leq n\)

\(1\leq a\leq n\)

\(−1e6\leq val_i,x\leq 1e6\)

建议使用 scanf 读入

题解

知识点:DFS序,线段树。

用dfs序可以将树转换成包含子树信息的线性序列,只确定子树结束时间即可。那么,一个子树的根节点开始时间和结束时间之间的节点,都是这个子树的节点。随后,可以用线段树处理这个序列了。

时间复杂度 \(O(n + m\log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; struct Graph {
struct edge {
int v, nxt;
};
int idx;
vector<int> h;
vector<edge> e; Graph(int n = 0, int m = 0) { init(n, m); } void init(int n, int m) {
idx = 0;
h.assign(n + 1, 0);
e.assign(m + 1, {});
} void add(int u, int v) {
e[++idx] = { v,h[u] };
h[u] = idx;
}
}; struct T {
ll sum;
static T e() { return { 0 }; }
friend T operator+(const T &a, const T &b) { return { a.sum + b.sum }; }
}; struct F {
ll add;
T operator()(const T &x) { return { x.sum + add }; }
}; template<class T, class F>
class SegmentTree {
int n;
vector<T> node; void update(int rt, int l, int r, int x, F f) {
if (r < x || x < l) return;
if (l == r) return node[rt] = f(node[rt]), void();
int mid = l + r >> 1;
update(rt << 1, l, mid, x, f);
update(rt << 1 | 1, mid + 1, r, x, f);
node[rt] = node[rt << 1] + node[rt << 1 | 1];
} T query(int rt, int l, int r, int x, int y) {
if (r < x || y < l) return T::e();
if (x <= l && r <= y) return node[rt];
int mid = l + r >> 1;
return query(rt << 1, l, mid, x, y) + query(rt << 1 | 1, mid + 1, r, x, y);
} public:
SegmentTree(int _n = 0) { init(_n); }
SegmentTree(const vector<T> &src) { init(src); } void init(int _n) {
n = _n;
node.assign(n << 2, T::e());
}
void init(const vector<T> &src) {
assert(src.size() >= 2);
init(src.size() - 1);
function<void(int, int, int)> build = [&](int rt, int l, int r) {
if (l == r) return node[rt] = src[l], void();
int mid = l + r >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
node[rt] = node[rt << 1] + node[rt << 1 | 1];
};
build(1, 1, n);
} void update(int x, F f) { update(1, 1, n, x, f); } T query(int x, int y) { return query(1, 1, n, x, y); }
}; const int N = 1000007;
Graph g;
int val[N]; int dfncnt;
int pos[N], lst[N];
void dfs(int u, int fa) {
pos[u] = ++dfncnt;
for (int i = g.h[u];i;i = g.e[i].nxt) {
int v = g.e[i].v;
if (v == fa) continue;
dfs(v, u);
}
lst[u] = dfncnt;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m, k;
cin >> n >> m >> k;
g.init(n, n << 1);
for (int i = 1;i <= n;i++) cin >> val[i];
for (int i = 1;i <= n - 1;i++) {
int u, v;
cin >> u >> v;
g.add(u, v);
g.add(v, u);
}
dfs(k, 0);
vector<T> src_val(n + 1);
for (int i = 1;i <= n;i++) src_val[pos[i]] = { val[i] };
SegmentTree<T, F> sgt(src_val);
while (m--) {
int op, u;
cin >> op >> u;
if (op == 1) {
int x;
cin >> x;
sgt.update(pos[u], { x });
}
else cout << sgt.query(pos[u], lst[u]).sum << '\n';
}
return 0;
}

NC204871 求和的更多相关文章

  1. Java程序:从命令行接收多个数字,求和并输出结果

    一.设计思想:由于命令行接收的是字符串类型,因此应先将字符串类型转化为整型或其他字符型,然后利用for循环求和并输出结果 二.程序流程图: 三.源程序代码:   //王荣荣 2016/9/23     ...

  2. Java之递归求和的两张方法

    方法一: package com.smbea.demo; public class Student { private int sum = 0; /** * 递归求和 * @param num */ ...

  3. EXCEL中对1个单元格中多个数字求和

    如A1=3779.3759.3769.3781.3750,A2对A1中4个数字求和怎么求!请高手赐教! 方法一:在B1中输入公式=SUM(MID(A1,{1,6,11,16,21},4)*1) 方法二 ...

  4. codevs 1082 线段树区间求和

    codevs 1082 线段树练习3 链接:http://codevs.cn/problem/1082/ sumv是维护求和的线段树,addv是标记这歌节点所在区间还需要加上的值. 我的线段树写法在运 ...

  5. 从sum()求和引发的思考

    sum()求和是一个非常简单的函数,以前我的写法是这样,我想大部分和我一样刚开始学习JS的同学写出来的也会是这样. function sum() { var total=null; for(var i ...

  6. //给定N个整数序列{A1,A2,A3...An},求函数f(i,j)=(k=i~j)Ak的求和

    //给定N个整数序列{A1,A2,A3...An},求函数f(i,j)=(k=i~j)Ak的求和 # include<stdio.h> void main() { ,sum1; ]={,- ...

  7. Ajax中get请求和post请求

    我们在使用Ajax向服务器发送数据时,可以采用Get方式请求服务器,也可以使用Post方式请求服务器,那么什么时候该采用Get方式,什么时候该采用Post方式呢? Get请求和Post请求的区别: 1 ...

  8. POJ 2823 Sliding Window 线段树区间求和问题

    题目链接 线段树区间求和问题,维护一个最大值一个最小值即可,线段树要用C++交才能过. 注意这道题不是求三个数的最大值最小值,是求k个的. 本题数据量较大,不能用N建树,用n建树. 还有一种做法是单调 ...

  9. HDU 1166 敌兵布阵 线段树单点更新求和

    题目链接 中文题,线段树入门题,单点更新求和,建一棵树就可以了. #include <iostream> #include <cstdio> #include <cmat ...

  10. F#之旅3 - F# PK C#:简单的求和

    原文链接:https://swlaschin.gitbooks.io/fsharpforfunandprofit/content/posts/fvsc-sum-of-squares.html Comp ...

随机推荐

  1. Redis 集群模式搭建

    本文为博主原创,未经允许不得转载: 目录: 1. 哨兵模式与集群模式对比 2. Redis 集群架构搭建 3. 集群原理分析 4. 集群元数据维护方式对比 5. redis 分布式寻址 6. 集群选举 ...

  2. 基于java+springboot的家教预约网站、家教信息管理系统

    该系统是基于java+springboot开发的家教预约网站.是给师妹开发的实习作品.学习过程中,遇到问题可以在github咨询作者. 演示地址 前台地址: http://jiajiao.gitapp ...

  3. SD Host控制器微架构设计

    微架构设计思路 ahb_slave_if中的寄存器可以在datasheet中进行描述 sd_clk - 时钟产生模块的接口描述 sd_data_fsm和sd_cmd_fsm - 状态机描述 发送时序需 ...

  4. pycharm设置保存时自动格式化代码(Auto Reformat Code)

    原文:https://blog.csdn.net/qq_41906934/article/details/124631826 1.手动格式化代码 Code -> Reformat Code 格式 ...

  5. idea侧边栏commit消失

    1.问题 在使用Clion中想要查看提交的相关信息,发现侧边栏commit消失 2.解决方法 打开设置,选择会用非模式提交界面即可

  6. 【linux】虚拟机 ubuntu 使用 sudo apt-get install 安装软件出现 “Unable to locate package xxx ”解决方法

    使用 sudo apt-get install 安装软件出现如下错误 上述错误表示找不到软件源,可更改软件源服务器解决 还有工具链 arm-none-eabi-gcc 实际安装的是 sudo apt ...

  7. Mybatis @Insert插入数据返回自增的主键id

    mapper层 @Insert("insert into t_user (username,password,valid,create_time) values (#{username},# ...

  8. Linux-文件权限-rwx-chmod

  9. [转帖]Always-on Profiling for Production Systems

    https://0x.tools/ 0x.tools (GitHub) is a set of open-source utilities for analyzing application perf ...

  10. [转帖]Linux之bash反弹shell原理浅析

    环境 攻击机:kali            ip:192.168.25.144 靶    机:centos      ip:192.168.25.142 过程 kali 监听本地8888端口 靶机 ...