题目大意:给定一棵树,每个点有点权。有m个操作:
- 操作 1 :把某个节点 x 的点权增加 a 。
- 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
- 操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
定睛一看,这不就是树链剖分的模板题吗?!连问题都是一样的。。。(树链剖分模板)不过因为我写的不熟,只能大力线段树。。。
思路:首先把树的DFS序求出来,这样就把树上的操作转化为区间操作,再记录一下进栈出栈顺序。随后构建线段树,每段区间记录进栈点和出栈点个数。
操作1:进栈点,出栈点修改。
操作2:找到这个点的进栈,出栈,再区间加。
操作3:直接求根到进栈点的前缀和。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 100001;
int w[MAXN];
int Begin[MAXN], End[MAXN];
int fir[MAXN], nxt[MAXN << 1], to[MAXN << 1], cnt;
int a[MAXN << 1], cur, flag[MAXN << 1];
struct node{
int l, r;
int num[2];//1表示入栈,0表示出栈
long long data, tag;
}t[MAXN << 3];
inline int read(){
int k = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){k = k*10 + ch - '0'; ch = getchar();}
return k * f;
}
inline void add_edge(int a, int b){
to[cnt] = b;
nxt[cnt] = fir[a];
fir[a] = cnt++;
}
inline void pushup(int u){
t[u].data = t[u << 1].data + t[u << 1 | 1].data;
}
inline void add(int u, long long k){
t[u].tag += k;
t[u].data += (long long)(t[u].num[1] - t[u].num[0]) * k; //进栈点个数减出栈点个数
}
inline void pushdown(int u){
if(t[u].tag){
add(u << 1, t[u].tag);
add(u << 1 | 1, t[u].tag);
t[u].tag = 0;
}
}
void build(int u, int L, int R){
t[u].l = L, t[u].r = R;
if(L == R){
t[u].num[flag[L]]++;
t[u].data = flag[L] ? w[a[L]] : -w[a[L]];
return;
}
int mid = (L + R) >> 1;
build(u << 1, L, mid);
build(u << 1 | 1, mid + 1, R);
t[u].num[0] = t[u << 1].num[0] + t[u << 1 | 1].num[0];
t[u].num[1] = t[u << 1].num[1] + t[u << 1 | 1].num[1];
pushup(u);
}
void change(int u, int L, int R, long long k){
if(t[u].l >= L && t[u].r <= R){
add(u, k);
return;
}
pushdown(u);
if(t[u << 1].r >= L) change(u << 1, L, R, k);
if(t[u << 1 | 1].l <= R) change(u << 1 | 1, L, R, k);
pushup(u);
}
long long query(int u, int L, int R){
if(t[u].l >= L && t[u].r <= R)
return t[u].data;
pushdown(u);
long long ans = 0;
if(t[u << 1].r >= L) ans += query(u << 1, L, R);
if(t[u << 1 | 1].l <= R) ans += query(u << 1 | 1, L, R);
return ans;
}
void dfs(int u, int fa){
a[++cur] = u;
flag[cur] = 1;
Begin[u] = cur;
for(int i = fir[u]; i != -1; i = nxt[i]){
int v = to[i];
if(v == fa) continue;
dfs(v, u);
}
a[++cur] = u;
flag[cur] = 0;
End[u] = cur;
}
int main(){
memset(fir, -1, sizeof(fir));
int n = read(), m = read();
for(int i = 1; i <= n; i++)
w[i] = read();
for(int i = 1; i < n; i++){
int a = read(), b = read();
add_edge(a, b);
add_edge(b, a);
}
dfs(1, 0);
build(1, 1, cur);
for(int i = 1; i <= m; i++){
int opt = read(), x = read(), k;
switch(opt){
case 1:
k = read();
change(1, Begin[x], Begin[x], k);
change(1, End[x], End[x], k);
break;
case 2:
k = read();
change(1, Begin[x], End[x], k);
break;
case 3:
printf("%lld\n", query(1, 1, Begin[x]));
break;
}
}
return 0;
}
鸽了几天了,咕咕咕~