poj 3321 Apple Tree 树状数组 dfs序

10 篇文章 0 订阅
8 篇文章 0 订阅

题目链接


参考资料:

http://www.cnblogs.com/gj-Acit/p/3236843.html   ——再见~雨泉


题意:

给一棵树,初始时每个节点的 val 均为 1,现有一些操作,

Q:将某个节点的 val 取反

C:询问以某个节点为根的子树的 val 总和


思路:

dfs 一遍,将每个点的编号映射到 dfs 序,以 dfs 序来建树状数组。

每个点顺便记录两个值,le[ ] 为其自身的 dfs 序,ri[ ] 为其孩子中最大的 dfs 序。

那么查询某个节点为根的子树即为树上 le[ ] 到 ri[ ] 这连续的一段,直接用树状数组求两个前缀和作差即可。

关键的想法还是编号映射到 dfs 序。


Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 100010
using namespace std;
typedef long long LL;
struct Edge {
    int to, ne;
    Edge(int a = 0, int b = 0) : to(a), ne(b) {}
}edge[maxn * 2];
int le[maxn], ri[maxn], ne[maxn], c[maxn], tot, cnt, n;
bool flag[maxn];
inline int max(int a, int b) { return a > b ? a : b; }
void add(int u, int v) {
    edge[tot] = Edge(v, ne[u]);
    ne[u] = tot++;
}
void dfs(int u, int fa) {
    le[u] = ++cnt;
    int maxx = cnt;
    for (int i = ne[u]; i != -1; i = edge[i].ne) {
        Edge e = edge[i]; int v = e.to;
        if (v == fa) continue;
        dfs(v, u);
        maxx = max(maxx, ri[v]);
    }
    ri[u] = maxx;
}
inline int lowbit(int x) { return x & (-x); }
int query(int x) {
    int ret = 0;
    while (x) {
        ret += c[x];
        x -= lowbit(x);
    }
    return ret;
}
void modify(int x, int delta) {
    while (x <= n) {
        c[x] += delta;
        x += lowbit(x);
    }
}
void work() {
    memset(c, 0, sizeof(c)); memset(ne, -1, sizeof(ne)); tot = cnt = 0;
    for (int i = 1; i <= n; ++i) { modify(i, 1); flag[i] = 1; }
    for (int i = 1; i < n; ++i) {
        int x, y;
        scanf("%d%d", &x, &y);
        add(x, y); add(y, x);
    }
    dfs(1, -1);
    int q;
    scanf("%d", &q);
//    for (int i = 1; i <= n; ++i) printf("%d %d\n", le[i], ri[i]);
    while (q--) {
        char ch; int x;
        scanf("\n%c %d", &ch, &x);
        if (ch == 'Q') {
            printf("%d\n", query(ri[x]) - (le[x] == 1 ? 0 : query(le[x] - 1)));
        }
        else {
            x = le[x];
            if (flag[x]) modify(x, -1);
            else modify(x, 1);
            flag[x] ^= 1;
        }
    }
}
int main() {
//    freopen("in.txt", "r", stdin);
    while (scanf("%d", &n) != EOF) work();
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值