cf 383C Propagating tree - dfs序 + 线段树

传送门

只能说这题很妙。

给出一棵树,树上有权值,现在你对一个结点加\(x\),那么他的孩子结点会加\(-x\),依次类推。

操作有单点加,单点查询。

实际上可以把树按照深度划分为奇和偶。定义奇数的加,偶数的减。

如果说深度为奇数,那么就把它的所有孩子结点都加\(x\),如果说深度为偶数,那么就把它的所有孩子结点都加\(-x\)。
查询时,同样这样,如果深度是奇数,那么就把答案乘1,否则乘-1。

对于深度为偶数的,加的时候是负数,查询的时候再乘以负号,答案不变。如果有贡献是由深度为奇数的加上的,那么加的一定是正数,最后乘负号,答案不变。
同理,对于深度为奇数的,加的时候是正数,查询的时候是正数。如果有贡献来自深度为偶数的,那么加的值肯定是负数。

相当于变成了线段树区间加操作和单点查询操作了。

树上线段树操作,可以用树链剖分,但是大材小用了,就直接利用dfs序+线段树即可。

#include <bits/stdc++.h>
#define ll long long
#define CASE int Kase = 0; cin >> Kase; for(int kase = 1; kase <= Kase; kase++)
using namespace std;
template<typename T = long long> inline T read() {
    T s = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
    while(isdigit(ch)) {s = (s << 3) + (s << 1) + ch - 48; ch = getchar();} 
    return s * f;
}
const int N = 2e5 + 5, M = 1e6 + 5, MOD = 1e9 + 7, CM = 998244353, INF = 0x3f3f3f3f;
struct Edge {
    int to, next;
}e[M << 1];
int head[N], tot;
void add(int u, int v){
    e[++tot].to = v;
    e[tot].next = head[u];
    head[u] = tot;
}
int a[N];
struct SegTree{
    struct Node{
        int l, r;
        int val, lazy;
        #define l(p) tree[p].l
        #define r(p) tree[p].r
        #define lson (p << 1)
        #define rson (p << 1 | 1)
        #define val(p) tree[p].val
        #define lazy(p) tree[p].lazy
    } tree[N << 2];
    void pushup(int p){
        val(p) = val(lson) + val(rson);
    }
    void pushdown(int p){
        if(lazy(p)) {
            lazy(lson) += lazy(p);
            lazy(rson) += lazy(p);
            val(lson) += (r(lson) - l(lson) + 1) * lazy(p);
            val(rson) += (r(rson) - l(rson) + 1) * lazy(p);
            lazy(p) = 0;
        }
    }
    void build(int p, int l, int r){
        l(p) = l, r(p) = r;
        lazy(p) = 0, val(p) = 0;
        if(l(p) == r(p)) {
            return;
        }
        int mid = (l + r) >> 1;
        build(lson, l, mid); build(rson, mid + 1, r);
        pushup(p);
    }
    void Change(int p, int l, int r, int x) {
        if(l <= l(p) && r(p) <= r) {
            lazy(p) += x;
            val(p) += (r(p) - l(p) + 1) * x;
            return;
        }
        pushdown(p);
        int mid = (l(p) + r(p)) >> 1;
        if(l <= mid) Change(lson, l, r, x);
        if(r > mid) Change(rson, l, r, x);
        pushup(p);
    }
    int Query(int p, int x) {
        if(l(p) == r(p)) return val(p);
        pushdown(p);
        int mid = (l(p) + r(p)) >> 1;
        if(x <= mid) return Query(lson, x);
        else return Query(rson, x);
    }
} seg;
int dep[N], l[N], r[N], id;
void dfs(int u, int fath){
    dep[u] = dep[fath] + 1; l[u] = ++id;
    for(int i = head[u]; i; i = e[i].next) {
        int v = e[i].to;
        if(v == fath) continue;
        dfs(v, u);
    }
    r[u] = id;
}
void subtask1() {
    int x = read(), val = read();
    seg.Change(1, l[x], r[x], dep[x] & 1 ? val : -val);
}
void subtask2() {
    int x = read();
    printf("%d\n", a[x] + seg.Query(1, l[x]) * (dep[x] & 1 ? 1 : -1));
}
void solve(int kase){
    int n = read(), m = read();
    for(int i = 1; i <= n; i++) a[i] = read();
    for(int i = 1; i < n; i++) {
        int u = read(), v = read();
        add(u, v); add(v, u);
    }
    dfs(1, 0);
    seg.build(1, 1, n);
    for(int i = 1; i <= m; i++) {
        int op = read();
        if(op == 1) subtask1();
        if(op == 2) subtask2();
    }
}
const bool DUO = 0;
int main(){
    clock_t start, finish; double totaltime; start = clock();
    if(DUO) {CASE solve(kase);} else solve(1);
    finish = clock(); 
    #ifdef ONLINE_JUDGE
        return 0;
    #endif
    printf("\nTime: %lfms\n", (double)(finish - start) / CLOCKS_PER_SEC * 1000);
    return 0;
}
上一篇:OI日记-2021


下一篇:cf每日三题补题