POJ3321Apple Tree(DFS序 +树状数组)

Apple Tree
题意:给出一棵以1为根节点,有n个节点的树,每个节点存有 0   o r   1 0~or~1 0 or 1。有两种操作,(1) Q , x Q,x Qx。(2) C , x C,x Cx
(1).查询以第x个节点为根节点的子树,所有节点的和为多少。
(2).将第x个节点取反,0变1,1变0。
给出m个操作,并在树上完成些操作。

d f s dfs dfs

d f s 序 dfs序 dfs,简单理解:通过 d f s dfs dfs遍历得到的一个序列。其实是两个序列,一个存的是遍历到第 i i i个节点的时刻,一个存的是遍历完第 i i i个节点的所有子节点的时刻
在代码中用 l [ N ] l[N] l[N] r [ N ] r[N] r[N],分别表示。(这也是个模板,先不考虑它的用途)

void dfs(int ne) {
    
    
    l[ne] = tot;//记录遍历到第i个节点的时刻。
    for(int i=head[ne]; i; i=edge[i].next) {
    
    
        tot++;
        dfs(edge[i].to);
    }
    r[ne] = tot;//记录遍历完所有子节点的时刻。
}

参考博客:DFS序——树链剖分前驱知识

树状数组

树状数组(入门)

思路:现在先说一下dfs序在这题中有啥作用。
图 片 转 载 于 图片转载于 POJ3321 Apple Tree (树状数组)

其中构成的 d f s dfs dfs序就是,形如 ( l [ i ] , r [ i ] ) {(l[i],r[i])} (l[i],r[i]),i从1到6分别为 [ 1 , 6 ] [ 2 , 4 ] [ 3 , 3 ] [ 4 , 4 ] [ 5 , 6 ] [ 6 , 6 ] [1,6] [2,4] [3,3] [4,4] [5,6] [6,6] [1,6][2,4][3,3][4,4][5,6][6,6] ( l [ i ] , r [ i ] ) {(l[i],r[i])} (l[i],r[i])第一个元素就是该节点所在位置,第二个元素是它能够管辖的最后一个元素下标(也就是以第i个节点为根的子树中最后的一个元素)。

这样一来。我们只要用树状数组维护每个节点中的值( 0   o r   1 0~or~1 0 or 1)。b = query(r[a]) - query(l[a]-1)中第一个 q u e r y ( r [ a ] ) query(r[a]) query(r[a]),求的是以 1 1 1为根节点 r [ a ] r[a] r[a]为最后一个节点的树的节点总和,同理 q u e r y ( l [ a ] − 1 ) query(l[a]-1) query(l[a]1)。用 q u e r y ( r [ a ] ) − q u e r y ( l [ a ] − 1 ) query(r[a])-query(l[a]-1) query(r[a])query(l[a]1)就是再节点x的管辖范围下节点值得和。
将思路概况一下,就是 d f s dfs dfs d f s dfs dfs序,用 d f s dfs dfs序的特点和树状数组求和。

代码:

// poj3233
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#include<queue>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
const ll INF = 0x7ffffffffffff;
const int inf = 0x7ffffff;
int read() {
    
    //快读模板。
    int x = 0; int f = 1; char s = getchar();
    while(s < '0' || s > '9') {
    
    if(s == '-') f = -1; s = getchar();}
    while(s >= '0' && s <= '9') {
    
    x = (x << 3) + (x << 1) + s - 48; s = getchar();}
    return x * f;
}

int head[N], vis[N], idx = 1;
int l[N], r[N], tot = 1, T[N], A[N];
struct E{
    
    //链式前向星存树
    int to, next;
}edge[2*N];
void add(int a, int b) {
    
    
    edge[idx].to = b, edge[idx].next = head[a], head[a] = idx++;
}

void dfs(int ne) {
    
    //普通dfs。
    l[ne] = tot;//记录遍历到第i个节点的时刻。
    for(int i=head[ne]; i; i=edge[i].next) {
    
    
        tot++;
        dfs(edge[i].to);
    }
    r[ne] = tot;//记录遍历完所有子节点的时刻。
}
//向树状数组中添加元素
void fills(int x, int val, int n) {
    
    
    while(x <= n) {
    
    
        T[x] += val;
        x += x & (-x);
    }
}
//询问节点总和。
int query(int x) {
    
    
    int ans = 0;
    while(x) {
    
    
        ans += T[x];
        x -= x & (-x);
    }
    return ans;
}
int main() {
    
    
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    int n, m, a, b;
    char c;
    while(scanf("%d", &n) != EOF) {
    
    
    //初始化,特别重要
        memset(edge, 0, sizeof edge);
        memset(head, 0, sizeof head);
        idx = 1, tot = 1;
        memset(T, 0, sizeof T);
        for(int i=1; i<n; i++) {
    
    
            scanf("%d%d", &a, &b);
            add(a, b);
        }
        dfs(1);//dfs找dfs序
        for(int i=1; i<=n; i++) {
    
    
            A[i] = 1;
            fills(i, 1, n);
        }
        scanf("%d%*c", &m);//输入挺恶心的,注意%*c吃掉回车
        for(int i=0; i<m; i++) {
    
    
            scanf("%c %d%*c", &c, &a);//同上
            if(c == 'C') {
    
    
                if(A[a]) {
    
    
                    A[a] = 0;
                    fills(l[a], -1, n);
                }
                else {
    
    
                    A[a] = 1;
                    fills(l[a], 1, n);
                }
            }
            else {
    
    
                b = query(r[a]) - query(l[a]-1);
                printf("%d\n", b);
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45363113/article/details/107261861