题目概述
给你一棵树,节点初始权值是0.要求实现两种操作
- 路径u到v上所有节点的权值增加某个值
- 询问操作,询问以u为根的所有子树的权值之和(包括u自身)
题目链接
分析
树链剖分,模板题,我只会做模板题,(笑
附上上一篇模板的链接
传送门
本题由于点权的初始值时0,建树操作实际上可以不进行,不过还是走一下程序比较完整。有一点需要注意的是节点的编号是从0开始的,如果不做任何处理的话会对链式前向星加边以及dfs时造成影响,方便起见,我们将每一个边的编号+1,转化成我们熟悉的形式。剩下的就是模板了。
代码
#include <cctype>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define mid (l + r >> 1)
#define len (r - l + 1)
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n, m, tot, cnt;
int head[N];
int par[N], size[N], dep[N], son[N];
int dfn[N], pre[N], top[N];
inline int read(){
int x = 0, op = 1;
char ch = getchar();
while (!isdigit(ch)){
if (ch == '-') op = -1;
ch = getchar();
}
while (isdigit(ch)){
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * op;
}
struct edge{
int to, nxt;
}e[N << 1];
inline void add_edge(int u, int v){
e[++tot] = {v, head[u]};
head[u] = tot;
}
void dfs1(int u, int fa){
par[u] = fa;
size[u] = 1;
dep[u] = dep[fa] + 1;
for (int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].to;
if (v == fa) continue;
dfs1(v, u);
size[u] += size[v];
if (size[v] > size[son[u]])
son[u] = v;
}
}
void dfs2(int u, int tp){
top[u] = tp;
dfn[u] = ++cnt;
pre[cnt] = u;
if (son[u])
dfs2(son[u], tp);
for (int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].to;
if (v != son[u] && v != par[u])
dfs2(v, v);
}
}
//线段树
ll sum[N << 2], add[N << 2];
inline void buildTree(int i, int l, int r){
if (l == r) {
sum[i] = 0;
return;
}
buildTree(i << 1, l, mid);
buildTree(i << 1|1, mid + 1, r);
}
inline void push_up(int i){
sum[i] = sum[i << 1] + sum[i << 1|1];
}
//push_down的区间长度要和建树时相对应
inline void push_down(int i, int l, int r){
if (add[i] == 0) return;
add[i << 1] += add[i];
add[i << 1|1] += add[i];
sum[i << 1] += add[i] * (mid - l + 1);
sum[i << 1|1] += add[i] * (r - mid);
add[i] = 0;
}
inline void add_interval(int i, int l, int r, int crtl, int crtr, int val){
if (l > crtr || r < crtl)
return;
if (l >= crtl && r <= crtr){
sum[i] += (ll)len * val;
add[i] += val;
return;
}
push_down(i, l, r);
if (mid >= crtl)
add_interval(i << 1, l, mid, crtl, crtr, val);
if (mid < crtr)
add_interval(i << 1|1, mid + 1, r, crtl, crtr, val);
push_up(i);
}
inline ll query_interval(int i, int l, int r, int crtl, int crtr){
if (l > crtr || r < crtl)
return 0;
if (l >= crtl && r <= crtr)
return sum[i];
push_down(i, l, r);
ll res = 0;
if (mid >= crtl)
res += query_interval(i << 1, l, mid, crtl, crtr);
if (mid < crtr)
res += query_interval(i << 1|1, mid + 1, r, crtl, crtr);
return res;
}
inline void tree_add(int u, int v, int d){
while (top[u] != top[v]){
if (dep[top[u]] < dep[top[v]])
swap(u, v);
add_interval(1, 1, n, dfn[top[u]], dfn[u], d);
u = par[top[u]];
}
if (dep[u] > dep[v])
swap(u, v);
add_interval(1, 1, n, dfn[u], dfn[v], d);
}
int main() {
n = read();
memset(head, -1, sizeof(head));
for (int i = 1, x, y; i <= n - 1; ++i) {
x = read(), y = read();
add_edge(x + 1, y + 1), add_edge(y + 1, x + 1);
}
dfs1(1, -1);
dfs2(1, 1);
buildTree(1, 1, n);
m = read();
char ch[2];
int x = 0, y = 0, z = 0;
while (m--){
scanf("%s", &ch);
if (ch[0] == 'A'){
x = read(), y = read(), z = read();
tree_add(x + 1, y + 1, z);
}else if (ch[0] == 'Q'){
x = read();
printf("%lld\n",query_interval(1, 1, n, dfn[x + 1], dfn[x + 1] + size[x + 1] - 1));
}
}
return 0;
}
测试样例
/*
*
input:
10
0 1
0 7
1 2
1 5
7 9
2 3
3 4
3 6
6 8
5
A 4 9 1
Q 0
A 9 0 10
Q 0
Q 7
output:
7
37
22
*/