题目:
这是一道模板题。
给定一棵n个节点的树,初始时该树的根为1号节点,每个节点有一个给定的权值。下面依次进行m个操作,操作分为如下五种类型:
换根:将一个指定的节点设置为树的新根。
修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。
修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。
询问路径:询问某条路径上节点的权值和。
询问子树:询问某个子树内节点的权值和。
贴个题目防止搜不到题。
感觉这题根本不是一道模板题。(自己就是这样被坑进来的)
看到换根我就蒙了。其实可以发现,路径与根是无关的。
据大佬手推,改根的情况可以分成两种:
- root不在u的子树中时 操作按常规进行。
- root在u的子树中时 除了root的子树和root与u中间这一坨点,u都可以遍及。
这点想了好久,之前想的是用u的字数和减去root的子树和u与root中间的一坨点。(我还想过root与u中间的一坨点是链 )
上马咯!
#include<cstdio>
#include<vector>
#include<iostream>
using namespace std;
const int N = 100002;
typedef long long ll;
int q, n, w[N], top[N], son[N], d[N], id[N], f[N], s[N], cnt, root = 1;
vector <int> G[N];
ll st[N << 2], la[N << 2];
void init(const int x, const int ba) {
d[x] = d[ba] + 1;
s[x] = 1;
f[x] = ba;
for(int i = 0; i < G[x].size(); i ++) {
int v = G[x][i];
if(v == ba)
continue;
init(v, x);
s[x] += s[v];
if(s[v] > s[son[x]] || son[x] == 0)
son[x] = v;
}
}
void dfs(const int x, const int t) {
top[x] = t;
id[x] = ++ cnt;
if(son[x] == 0)
return;
dfs(son[x], t);
for(int i = 0; i < G[x].size(); i ++) {
int v = G[x][i];
if(v == f[x] || v == son[x])
continue;
dfs(v, v);
}
}
void pushDown(const int x, const int L, const int R) {
if(! la[x])
return;
int l = (x << 1), r = (x << 1 | 1), mid = L + R >> 1;
st[l] += la[x] * (mid - L + 1);
st[r] += la[x] * (R - mid);
la[l] += la[x];
la[r] += la[x];
la[x] = 0;
}
void add(const int x, const int l, const int r, const int L, const int R, const int k) {
if(l > R || r < L)
return;
if(l >= L && r <= R) {
st[x] += 1ll * k * (r - l + 1);
la[x] += k;
return;
}
pushDown(x, l, r);
int mid = l + r >> 1;
add(x << 1, l, mid, L, R, k);
add(x << 1 | 1, mid + 1, r, L, R, k);
st[x] = st[x << 1] + st[x << 1 | 1];
}
ll ask(const int x, const int l, const int r, const int L, const int R) {
if(l > R || r < L)
return 0;
if(l >= L && r <= R)
return st[x];
pushDown(x, l, r);
int mid = l + r >> 1;
return ask(x << 1, l, mid, L, R) + ask(x << 1 | 1, mid + 1, r, L, R);
}
int main() {
int tt, flag, a, b;
ll ans;
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
scanf("%d", &w[i]);
for(int i = 1; i < n; i ++) {
scanf("%d", &tt);
G[tt].push_back(i + 1);
}
init(1, 0);
dfs(1, 1);
for(int i = 1; i <= n; i ++)
add(1, 1, n, id[i], id[i], w[i]);
scanf("%d", &q);
while(q --) {
scanf("%d", &flag);
switch(flag) {
case 1 : {
scanf("%d", &tt);
root = tt;
break;
}
case 2 : {
scanf("%d %d %d", &a, &b, &tt);
while(top[a] != top[b]) {
if(d[top[a]] <= d[top[b]]) {
add(1, 1, n, id[top[b]], id[b], tt);
b = f[top[b]];
}
else {
add(1, 1, n, id[top[a]], id[a], tt);
a = f[top[a]];
}
}
if(id[a] <= id[b])
add(1, 1, n, id[a], id[b], tt);
else
add(1, 1, n, id[b], id[a], tt);
break;
}
case 3 : {
scanf("%d %d", &a, &tt);
if(id[a] + s[a] - 1 >= id[root] && id[a] < id[root]) {
add(1, 1, n, 1, n, tt);
b = root;
while(a != f[b])
b = f[b];
add(1, 1, n, id[b], id[b] + s[b] - 1, -tt);
}
else if(root == a)
add(1, 1, n, 1, n, tt);
else
add(1, 1, n, id[a], id[a] + s[a] - 1, tt);
break;
}
case 4 : {
ans = 0;
scanf("%d %d", &a, &b);
while(top[a] != top[b]) {
if(d[top[a]] < d[top[b]]) {
ans += ask(1, 1, n, id[top[b]], id[b]);
b = f[top[b]];
}
else {
ans += ask(1, 1, n, id[top[a]], id[a]);
a = f[top[a]];
}
}
if(id[a] < id[b])
ans += ask(1, 1, n, id[a], id[b]);
else
ans += ask(1, 1, n, id[b], id[a]);
printf("%lld\n", ans);
break;
}
default : {
scanf("%d", &a);
if(id[a] + s[a] - 1 >= id[root] && id[a] < id[root]) {
ans = ask(1, 1, n, 1, n);
b = root;
while(a != f[b])
b = f[b];
printf("%lld\n", ans - ask(1, 1, n, id[b], id[b] + s[b] - 1));
}
else if(root == a)
printf("%lld\n", ask(1, 1, n, 1, n));
else
printf("%lld\n", ask(1, 1, n, id[a], id[a] + s[a] - 1));
break;
}
}
}
return 0;
}
记得特判。
好了就讲到这里了,如有错误请大佬在评论区留言,谢谢!