樹的DFS序

RonChen發表於2024-06-09

image

例題:P3459 [POI2007] MEG-Megalopolis

給定一棵 \(n\) 個節點的樹,根節點為 \(1\),開始每條邊邊權為 \(1\)。有 \(m+n-1\) 次操作,每次修改操作使得某條邊邊權為 \(0\),每次查詢操作詢問 \(1\) 到某個點的邊權和。
資料範圍:\(n \le 250000\)

如果從 DFS 序列的角度考慮,將每個節點在 DFS 中的第一個出現位置看作 +1,第二個位置看作 -1,則每次查詢相當於查詢序列的字首和,而修改操作相當於對該條邊的子節點在 DFS 序列中兩次出現的位置做單點更新。

#include <cstdio>
#include <vector>
using std::vector;
const int N = 250005;
vector<int> tree[N];
int n, in[N], out[N], idx, bit[N * 2];
char op[5];
int lowbit(int x) {
    return x & -x;
}
void update(int x, int d) {
    while (x <= 2 * n) {
        bit[x] += d; x += lowbit(x);
    }
}
int query(int x) {
    int res = 0;
    while (x > 0) {
        res += bit[x];
        x -= lowbit(x);
    }
    return res;
}
void dfs(int u, int fa) {
    idx++; in[u] = idx;
    update(idx, 1);
    for (int v : tree[u]) {
        if (v == fa) continue;
        dfs(v, u);
    }
    idx++; out[u] = idx;
    update(idx, -1);
}
int main()
{
    scanf("%d", &n);
    for (int i = 1; i < n; i++) {
        int a, b; scanf("%d%d", &a, &b);
        tree[a].push_back(b); tree[b].push_back(a);
    }
    dfs(1, 0);
    int m; scanf("%d", &m);
    for (int i = 1; i <= n + m - 1; i++) {
        scanf("%s", op);
        if (op[0] == 'A') {
            int x, y; scanf("%d%d", &x, &y);
            int z = in[x] < in[y] ? y : x;
            update(in[z], -1); update(out[z], 1);
        } else {
            int x; scanf("%d", &x);
            printf("%d\n", query(in[x]) - 1);
        }
    }
    return 0;
}

相關文章