牛客小白月赛12 H 华华和月月种树

题目链接:

题意:有三个操作

操作 1:表示节点 i 长出了一个新的儿子节点,权值为0,编号为当前最大编号 +1(也可以理解为,当前是第几个操作 1,新节点的编号就是多少)。
操作 2:表示华华上线做任务使节点 i 的子树中所有节点(即它和它的所有子孙节点)权值加 a 。
询问 3:华华需要给出 i 节点此时的权值。

解法:

把整棵树进行离线操作

先对这棵树进行dfs,一棵子树上的节点是连续的,加权值的时候直接把整颗子树加上权值即可,后面可以把新加上的点的权值清空。

当进行操作1的时候,把这个点的权值清零

当进行操作2的时候,把这个节点i的子树的所有结点权值加上a

当进行操作3的时候,查询这个结点i的权值

dfn[i]记录点i在树状数组上的位置,sz[i]是节点i的子树的大小

#include <bits/stdc++.h>
using namespace std;
const int M = 4e5 + 10;
int cnt, tot, n;
struct Edge{
    int next, to;
}edge[M * 2];
struct node{
    int opt, pos, x;
}a[M];
int dfn[M], sz[M], head[M], bit[M];
void add_egde(int u, int v) {
    //printf("u  %d v %d\n", u, v);
    edge[++tot].next = head[u];
    edge[tot].to = v;
    head[u] = tot;
}
int dfs(int u, int fa) {
    dfn[u] = ++cnt;
    sz[u] = 1;
    for(int i = head[u]; i; i = edge[i].next) {
        int v = edge[i].to;
        if(v == fa) continue;
        sz[u] += dfs(v, u);
    }
    return sz[u];
}
void update(int i, int x) {
    while(i <= n + 1) {
        bit[i] += x;
        i += i & (-i);
    }
}
void add(int l, int r, int val) {
    update(l, val);
    update(r + 1, -val);
}
int query(int i) {
    int ans = 0;
    while(i) {
        ans += bit[i];
        i -= i & (-i);
    }
    return ans;
}
int main(){
    int m;
    scanf("%d", &m);
    for(int i = 1; i <= m; i++) {
        scanf("%d%d", &a[i].opt, &a[i].pos);
        if(a[i].opt == 1) add_egde(a[i].pos, ++n), a[i].pos = n;
        if(a[i].opt == 2) scanf("%d", &a[i].x);
    }
    dfs(0, -1);
    for(int i = 1; i <= m; i++) {
        if(a[i].opt == 1) {
            int u = a[i].pos;
            int val = query(dfn[u]);
            add(dfn[u], dfn[u], -val);
        }
        else if(a[i].opt == 2) {
            int u = a[i].pos;
            int val = a[i].x;
            add(dfn[u], dfn[u] + sz[u] - 1, val);
        }
        else printf("%d\n", query(dfn[a[i].pos]));
    }
    return 0;
}
View Code
 

猜你喜欢

转载自www.cnblogs.com/linglinga/p/12031206.html