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 ...
随机推荐
- C# WPF:这次把文件拖出去!
首发公众号:Dotnet9 作者:沙漠之尽头的狼 编辑于:成都,2020-12-01 回顾上篇文章:C# WPF:把文件给我拖进来!!! 本文完成对应的下文:<C# WPF:这次把文件拖出去!& ...
- Nginx 配置文件备忘单
Nginx 是用于 Web 服务.反向代理.缓存.负载平衡.媒体流等的开源软件.在这篇文章中,我将提到一些我们经常使用的 Nginx 配置. 监听端口 server { Standard HTTP P ...
- 海思Hi35xx uboot启动分析总结
前言 在嵌入式linux设备中,uboot的最终目的就是启动kernel.对于uboot而言,没有人把它引导起来,所以uboot首先需要把自己加载起来,然后再去引导kernel的启动,这也就可以大致的 ...
- Django-Import-Export插件关于外键的处理
前言 Django-Import-Export是一款很好用很方便的Django数据导出导入插件,可以和DjangoAdmin管理后台完美集成,只需要少量的代码配置即可方便实现你要的多种格式导出导入,关 ...
- [转帖]Linux命令(51)——ipcs命令
https://cloud.tencent.com/developer/article/1380589 1.命令简介 ipcs命令用于报告Linux中进程间通信设施的状态,显示的信息包括消息列表.共享 ...
- [转帖]windos的kafka设置账号密码
1.kafka配置文件 server.properties增加 listeners=SASL_PLAINTEXT://127.0.0.1:9092 advertised.listeners=SASL_ ...
- Ergonomics JVM 的一种FullGC的说明
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/ergonomics.html 2 Ergonomics Ergo ...
- [转帖]AMD第四代宵龙 9174F 亮眼
https://www.amd.com/zh-hans/processors/epyc-9004-series#%E8%A7%84%E6%A0%BC 型号规格 型号 CPU 核心数量 线程数量 最 ...
- Rsync的简单使用
Rsync的简单使用 需求 一个运行很久的系统里面可能包含了非常多的垃圾文件. 但是又不可能随便删除, 很多垃圾可能有某些奇葩的用法. 有时候新建一个应用复制文件的话比较浪费磁盘和带宽. 所以这里简单 ...
- 使用Configmap 配置 springboot的application.yaml文件的方式部署环境的方法
Configmap部署k8s下Springboot服务的办法 前提 日常工作中需要使用k8s部署微服务环境, 但是内部的数据库连接和redis等连接非常麻烦,使用helm chart 进行变量替换时非 ...