mayoko’s diary

プロコンとかいろいろ。

Codeforces Round #225 (Div. 1) C. Propagating tree

昨日の練習会の D 問題は解法が「オイラーツアーしてセグメント木」という問題だったらしいんですが, そもそもオイラーツアーって名前しか聞いたことがなかったので, ちょっと練習してみました。

解法

オイラーツアー 木」でググるとわかりやすい資料がたくさん出てきたのでオイラーツアーについてはそれを参考にしてください。

オイラーツアーすると, 木 が 1 次元の配列に落とし込めるので, セグメント木のようなデータ構造が使いやすくなるわけです。

今回の問題を解く上でも, オイラーツアーしてできた配列について, 遅延評価つきセグメント木を使ってクエリを消化していきます。機能としては,
・ある区間 [l, r) に一様に値 x を加える
・ある区間 [l, r) の和を求める
というクエリを頂点数 n に対して  O(\log n) で計算できるようにするものです。よくあるやつなので, これも「セグメント木 遅延」とかでググれば情報がたくさん出てきます。

で, 今回の場合はある頂点 v は +val, v の子は -val, v の子の子は +val, ... というように値を足していくことになりますが, これは先ほど作ったオイラツアー配列ではどのように表現できるかというと, v からオイラーツアー配列で偶数個離れた頂点は +val を, 奇数個離れた頂点は -val を加えるようにすれば大丈夫です。

ということで, 2 つのセグメント木 even, odd を作って, 適当に even と odd に val または -val を加えていけば良いです。

// 遅延評価つきセグメント木
const ll INF = 1ll<<30;
template <typename T>
class segtree {
public:
    vector<T> seg;
    vector<T> lazy;
    int N;
    segtree(int V) {
        N = 1;
        while (N < V) N <<= 1;
        seg.resize(2*N-1);
        lazy.resize(2*N-1);
    }
    void lazy_evaluate(int k, int l, int r) {
        seg[k] += lazy[k]*(r-l);
        if (k < N-1) { // if k is not leaf
            lazy[2*k+1] += lazy[k];
            lazy[2*k+2] += lazy[k];
        }
        lazy[k] = 0;
    }
    void update(int a, int b, int k, int l, int r, T x) {
        if (r <= a || b <= l) return;
        if (a <= l && r <= b) {
            lazy[k] += x;
            lazy_evaluate(k, l, r);
        } else {
            lazy_evaluate(k, l, r);
            update(a, b, 2*k+1, l, (l+r)/2, x);
            update(a, b, 2*k+2, (l+r)/2, r, x);
            seg[k] = seg[2*k+1] + seg[2*k+2];
            return;
        }
    }
    T query(int a, int b, int k, int l, int r) {
        lazy_evaluate(k, l, r);
        if (r <= a || b <= l) return 0;
        if (a <= l && r <= b) return seg[k];
        else {
            T lch = query(a, b, 2*k+1, l, (l+r)/2);
            T rch = query(a, b, 2*k+2, (l+r)/2, r);
            return lch + rch;
        }
    }
    void update(int a, int b, T x) {
        update(a, b, 0, 0, N, x);
    }
    int query(int a, int b) {
        return query(a, b, 0, 0, N);
    }
};


// Euler-Tour
const int MAXSIZE = 200020;
int BEGIN[MAXSIZE], END[MAXSIZE];
vector<int> euler_tour;
int K;
vector<vi> G;

void createEulerTour(int v, int p) {
    BEGIN[v] = K++;
    euler_tour.push_back(v);
    for (int el : G[v]) {
        if (el == p) continue;
        createEulerTour(el, v);
        euler_tour.push_back(v);
        K++;
    }
    END[v] = K;
}
int a[MAXSIZE];

int main() {
    int n, m;
    cin >> n >> m;
    G.resize(n);
    segtree<int> even(2*n), odd(2*n);
    for (int i = 0; i < n; i++) {
        scanf("%d", a+i);
    }
    for (int i = 0; i < n-1; i++) {
        int b, c;
        scanf("%d%d", &b, &c);
        b--; c--;
        G[b].push_back(c);
        G[c].push_back(b);
    }
    createEulerTour(0, -1);
    for (int i = 0; i < n; i++) {
        if (BEGIN[i]%2 == 0) {
            even.update(BEGIN[i], BEGIN[i]+1, a[i]);
        } else {
            odd.update(BEGIN[i], BEGIN[i]+1, a[i]);
        }
    }
    while (m--) {
        int type;
        scanf("%d", &type);
        if (type == 1) {
            int x, val;
            scanf("%d%d", &x, &val);
            x--;
            if (BEGIN[x]%2 == 0) {
                even.update(BEGIN[x], END[x], val);
                odd.update(BEGIN[x], END[x], -val);
            } else {
                even.update(BEGIN[x], END[x], -val);
                odd.update(BEGIN[x], END[x], val);
            }
        } else {
            int x;
            scanf("%d", &x);
            x--;
            if (BEGIN[x]%2 == 0) {
                printf("%d\n", even.query(BEGIN[x], BEGIN[x]+1));
            } else {
                printf("%d\n", odd.query(BEGIN[x], BEGIN[x]+1));
            }
        }
    }
    return 0;
}