POJ 3321 Apple Tree(后根遍历将树转化成序列,用树状数组维护)

题意:一棵树,有很多分叉,每个分叉上最多有1个苹果。    

  给出n,接下来n-1行,每行u,v,表示分叉u,v之间有树枝相连。这里数据中u相当于树中的父节点,v相当于子节点。    

  给出两个操作:    

  1.C x  如果分叉x有一个苹果,表示该苹果被摘下;如果没苹果,表示该分叉长出1个苹果。    

  2.Q x  查询以分叉x为根节点的子树中所含有的苹果,包括分叉x。

思路:用树的后根遍历将树转化成一个序列,然后用树状数组维护。    

  树的后根遍历(先遍历每棵子树,再遍历根节点)实质上是按照自下而上、从左至右的顺序将非线性结构的树转化为一个线性序列。    

  每棵子树对应这个线性序列的一个连续的子区间,这样就可以用树状数组来维护更新。

原本我用vector超时,改用邻接表AC 。

 代码一:

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <vector>

using namespace std;
const int maxn=100010;
int n,m,val=0,tot=0; //val即为树状数组中的顺序编号,tot为邻接表的边序号
int c[maxn]; //树状数组
int a[maxn]; //a[i]表示分叉i的苹果的值,0或1
int hash_value[maxn];  //hash_value[i]表示分叉i在树状数组中的序号
int left_leaf[maxn];  //left_leaf[i]表示以i为根节点的子树中最左底端的叶子节点的编号,该值的hash_value即为以i为根节点的子树在树状数组中的区间的最左端
int head[maxn];

struct Edge {
    int to,next;
} edge[maxn];

void add(int u,int v) {
    edge[tot].next=head[u];
    edge[tot].to=v;
    head[u]=tot++;
}
int lowbit(int x) {
    return x&(-x);
}
void update(int i,int val) {
    while(i<=n) {
        c[i]+=val;
        i+=lowbit(i);
    }
}
int sum(int i) {
    int sum=0;
    while(i) {
        sum+=c[i];
        i-=lowbit(i);
    }
    return sum;
}
//dfs后根遍历,求取每个节点在树状数组中的顺序编号,以及每棵子树的left_leaf值
void dfs(int u) {
    if(head[u]==-1) {
        hash_value[u]=++val;
        left_leaf[u]=u;
        return;
    }
    int v,flag=1;
    for(int k=head[u]; k!=-1; k=edge[k].next) {
        v=edge[k].to;
        dfs(v);
        if(flag) {
            //这里的v即使父节点u的最先访问的子节点
            left_leaf[u]=left_leaf[v];
            flag=0;
        }
    }
    hash_value[u]=++val;
}
int main() {
    int u,v;
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    for(int i=1; i<n; i++) {
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    dfs(1);
    memset(c,0,sizeof(c));

    for(int i=1; i<=n; i++) {
        a[i]=1;
        update(i,1);
    }

    scanf("%d",&m);
    char str[5];
    int l,r;
    for(int i=0; i<m; i++) {
        scanf("%s%d",str,&u);
        if(str[0]=='Q') {
            r=hash_value[u];
            l=left_leaf[u];
            l=hash_value[l];
            //[l,r]即为以u为根节点的子树在树状数组中的区间
            printf("%d
",sum(r)-sum(l-1));
        } else {
            r=hash_value[u];
            if(a[r]==1) {
                a[r]=0;
                update(r,-1);
            } else {
                a[r]=1;
                update(r,1);
            }
        }
    }
    return 0;
}

也可以用结构体,来存储每个以节点i为根节点的子树在树状数组中的区间左端点和右端点:

代码二:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;
const int maxn=100005;
int n,m;
int c[maxn]; //树状数组
bool a[maxn];  //用来标记该节点是否有苹果
int cnt=1;
int head[maxn];
int tot=0;

struct Edge {
    int next,to;
} edge[maxn];

struct Apple {
    int l,r;  //apple[u]的l和r表示以u为根的子树在树状数组中对应区间
} apple[maxn];

void add(int u,int v) {
    edge[tot].next=head[u];
    edge[tot].to=v;
    head[u]=tot++;
}
//dfs,从节点u出发,计算即节点u为根的子树区间[apple[u].l,apple[u].r]
void dfs(int u) {
    if(head[u]==-1) {
        apple[u].l=apple[u].r=cnt;
        cnt++;
        return;
    }
    apple[u].l=cnt;
    for(int k=head[u]; k!=-1; k=edge[k].next) {
        int v=edge[k].to;
        dfs(v);
    }
    apple[u].r=cnt++;
}
int lowbit(int x) {
    return x&(-x);
}
void update(int i,int v) {
    while(i<=cnt) {
        c[i]+=v;
        i+=lowbit(i);
    }
}
int sum(int i) {
    int res=0;
    while(i) {
        res+=c[i];
        i-=lowbit(i);
    }
    return res;
}
int main() {
    char str[3];
    int u,v;
    memset(head,-1,sizeof(head));
    memset(a,true,sizeof(a));
    scanf("%d",&n);
    for(int i=0; i<n-1; i++) {
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    dfs(1);
    scanf("%d",&m);
    //初始时,每个节点数都有一个苹果
    for(int i=1; i<=n; i++)
        update(apple[i].r,1);
    for(int i=1; i<=m; i++) {
        scanf("%s%d",str,&u);
        if(str[0]=='C') {
            if(a[u]) {
                update(apple[u].r,-1);
                a[u]=false;
            } else {
                update(apple[u].r,1);
                a[u]=true;
            }
        } else {
            printf("%d
",sum(apple[u].r)-sum(apple[u].l-1));
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/chenxiwenruo/p/3348570.html