NC204871 求和
题目
题目描述
已知有 \(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 求和的更多相关文章
- Java程序:从命令行接收多个数字,求和并输出结果
一.设计思想:由于命令行接收的是字符串类型,因此应先将字符串类型转化为整型或其他字符型,然后利用for循环求和并输出结果 二.程序流程图: 三.源程序代码: //王荣荣 2016/9/23 ...
- Java之递归求和的两张方法
方法一: package com.smbea.demo; public class Student { private int sum = 0; /** * 递归求和 * @param num */ ...
- EXCEL中对1个单元格中多个数字求和
如A1=3779.3759.3769.3781.3750,A2对A1中4个数字求和怎么求!请高手赐教! 方法一:在B1中输入公式=SUM(MID(A1,{1,6,11,16,21},4)*1) 方法二 ...
- codevs 1082 线段树区间求和
codevs 1082 线段树练习3 链接:http://codevs.cn/problem/1082/ sumv是维护求和的线段树,addv是标记这歌节点所在区间还需要加上的值. 我的线段树写法在运 ...
- 从sum()求和引发的思考
sum()求和是一个非常简单的函数,以前我的写法是这样,我想大部分和我一样刚开始学习JS的同学写出来的也会是这样. function sum() { var total=null; for(var i ...
- //给定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; ]={,- ...
- Ajax中get请求和post请求
我们在使用Ajax向服务器发送数据时,可以采用Get方式请求服务器,也可以使用Post方式请求服务器,那么什么时候该采用Get方式,什么时候该采用Post方式呢? Get请求和Post请求的区别: 1 ...
- POJ 2823 Sliding Window 线段树区间求和问题
题目链接 线段树区间求和问题,维护一个最大值一个最小值即可,线段树要用C++交才能过. 注意这道题不是求三个数的最大值最小值,是求k个的. 本题数据量较大,不能用N建树,用n建树. 还有一种做法是单调 ...
- HDU 1166 敌兵布阵 线段树单点更新求和
题目链接 中文题,线段树入门题,单点更新求和,建一棵树就可以了. #include <iostream> #include <cstdio> #include <cmat ...
- F#之旅3 - F# PK C#:简单的求和
原文链接:https://swlaschin.gitbooks.io/fsharpforfunandprofit/content/posts/fvsc-sum-of-squares.html Comp ...
随机推荐
- Mathpix:屏幕截图 ➡ latex 公式,一键转换
安利一天能免费使用 10 次且好用的工具 Mathpix.
- pycharm设置保存时自动格式化代码(Auto Reformat Code)
原文:https://blog.csdn.net/qq_41906934/article/details/124631826 1.手动格式化代码 Code -> Reformat Code 格式 ...
- 【转】嵌入式C语言代码优化方案
来源:嵌入式C语言代码优化方案(深度好文,建议花时间研读并收藏) (qq.com) 1.选择合适的算法和数据结构 选择一种合适的数据结构很重要,如果在一堆随机存放的数中使用了大量的插入和删除指令,那使 ...
- MySQL调优学习-快速获取占用CPU较高的SQL语句
MySQL调优学习-快速获取占用CPU较高的SQL语句 背景 早上突然发现一个MySQL数据库的CPU使用率居高 因为是一个混布的环境上面还有一个redis 怕影响业务就上去像查看一下具体是何种原因导 ...
- [转帖]MySQL Connect/J 8.0时区陷阱
https://juejin.cn/post/6844904023015817224 最近公司正在升级Spring Boot版本(从1.5升级到2.1),其间踩到一个非常隐晦的MySQL时区陷阱, ...
- [转帖]linux块I/O总体概括
直接先上重点,linux中IO栈的完全图如下: 系统中能够随机访问固定大小数据片的硬件设备称作块设备.固定大小的数据片称为块.常见的块设备就是硬盘了.不能随机访问的就是字符设备了,管理块设备比字符设备 ...
- http 中使用 gzip 输出内容时,如何预先压缩前一半页面?
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 背景是这样:要输出一个很大的动态页面,不开 gzip 压缩 ...
- plcTIA Portal V16找不到许可证
首先快捷键win+s唤出搜索,搜:服务 其次搜索这个服务Automation License Manager Service 右击-启动服务,然后重新启动plc即可选择CPU型号了
- 【主流技术】聊一聊对 Mybatis Plus 的理解与应用
前言 mybatis plus是一个mybatis的增强工具,在其基础上只做增强不做改变.作为开发中常见的第三方组件,学习并应用在项目中可以节省开发时间,提高开发效率. 官方文档地址:MyBatis- ...
- 7.1 Windows驱动开发:内核监控进程与线程回调
在前面的文章中LyShark一直在重复的实现对系统底层模块的枚举,今天我们将展开一个新的话题,内核监控,我们以监控进程线程创建为例,在Win10系统中监控进程与线程可以使用微软提供给我们的两个新函数来 ...