Solution
比较经典的题型了吧, 我的做法是dfs序 + 树状数组
因为题目要求我们做到单点修改, 区间求和的一个操作
显然树状数组能够满足要求
那么问题便转化为如何把树上问题转化为简单的区间问题
考虑到每一个子树在dfs序下一定是连续的
我们可以保存它的编号最小点和编号最大点, 用来表示它的子树
比如, 对于 2 这个节点, 我们保存 2 和 4 表示以 2 为根这棵子树
剩下的就是很模板的树状数组区间求和, 单点更新了
具体看下代码吧
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long long LL;
const int N = 1e6 + 5;
const ll mod = 998244353;
const int DEG = 30;
const double PI = acos(-1.0);
const double eps = 1e-12;
const ll inf = 1e18;
static auto faster_iostream = []() {
std::ios::sync_with_stdio(false); // turn off sync
std::cin.tie(nullptr); // untie in/out streams
return 0;
}();
int T[N];
int lowbit(int x){
return x & (-x);
}
void update(int x, int add){
while(x < N){
T[x] += add;
x += lowbit(x);
}
}
ll query(int x){
ll res = 0;
while(x){
res += T[x];
x -= lowbit(x);
}
return res;
}
struct Edge{
int v, nextz;
}edge[N << 1];
int tot, head[N];
void init(){
memset(head, -1, sizeof(head));
}
void addedge(int u, int v){
edge[tot].v = v;
edge[tot].nextz = head[u];
head[u] = tot++;
}
int l[N], r[N];
int cnt = 0;
int a[N];
void dfs(int u, int par){
l[u] = ++cnt;
for(int i = head[u]; ~i; i = edge[i].nextz){
int v = edge[i].v;
if(v == par) continue;
dfs(v, u);
}
r[u] = cnt;
}
int main(){
init();
int n, m, root;
cin >> n >> m >> root;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n - 1; i++){
int u, v;
cin >> u >> v;
addedge(u, v);
addedge(v, u);
}
dfs(root, 0);
for(int i = 1; i <= n; i++) update(l[i], a[i]);
while(m--){
int op;
cin >> op;
int x, y;
if(op == 1){
cin >> x >> y;
update(l[x], y);
}
else {
cin >> x;
cout << query(r[x]) - query(l[x] - 1) << "\n";
}
}
return 0;
} 
京公网安备 11010502036488号