POJ 3321 Apple Tree (树状数组)

题目:http://poj.org/problem?id=3321

题意:给出一颗树,一开始树是满的(每个结点都有苹果)两种操作;1)C x ,x结点有苹果则摘掉这个苹果,否则加上一个苹果;2)Q x ,询问结点x为根的子树上共有多少苹果(包括x上的苹果)

思路:

关键是把这颗树转化为树状数组,一颗子树中的结点要占连续的空间,访问时才方便方法是dfs给各个结点编号。

每个结点对应两个编号:以这个结点为跟的子树所占的连续空间的开始位置s和结束位置e。

树状数组bit[ i ]记录从开头到 i 这一段的和,那么所求就是bit[ e ] - bit[ s ]。

才知道POJ用stl会很慢啊(大概是指针操作的问题吧),要用数组保存边信息建树,用vector会T。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <stdlib.h>
#define INF 0x7fffffff
#define MOD 1000000007
#include <vector>
using namespace std;
typedef long long ll;
const int MAXN = 100005;
vector<int> G[MAXN];
int n, m;
int s[MAXN], e[MAXN], cnt, apple[MAXN], bit[2 * MAXN];

struct Edge
{
    int to, next;
}edge[2 * MAXN];
int f[MAXN];
void addedge(int u, int v)
{
    edge[cnt].to = v;
    edge[cnt].next = f[u];
    f[u] = cnt++;
}
void dfs(int u)
{
    s[u] = cnt++;
    for(int i = f[u]; i != -1; i = edge[i].next)
    {
        if(!s[edge[i].to])
            dfs(edge[i].to);
    }
    e[u] = cnt++;
}

void update(int i, int x)
{
    //printf("update %d\n", i);
    while(i <= 2 * n)
    {
        bit[i] += x;
        i += i & -i;
    }
}

int sum(int i)
{
    int s = 0;
    while(i > 0)
    {
        s += bit[i];
        i -= i & -i;
    }
    return s;
}


int main()
{
    #ifdef LOCAL
    freopen("data.in", "r", stdin);
    #endif

    int u, v, x;
    char c;
    while(scanf("%d", &n) != EOF)
    {
        for(int i = 0; i <= n; i++)
            G[i].clear();
        memset(bit, 0, sizeof(bit));
        memset(s, 0, sizeof(s));
        memset(e, 0, sizeof(e));
        memset(apple, 0, sizeof(apple));

        memset(f, -1, sizeof(f));
        cnt = 0;
        for(int i = 0; i < n - 1; i++)
        {
            scanf("%d%d", &u, &v);
            //G[u].push_back(v); G[v].push_back(u);
            addedge(u, v);
            addedge(v, u);
        }

        cnt = 1;
        dfs(1);
        for(int i = 1; i <= n; i++)
        {
            //printf("%d:%d-%d\n", i, s[i], e[i]);
            update(e[i], 1);
            apple[i] = 1;
        }

        scanf("%d", &m);
        for(int i = 0; i < m; i++)
        {
            scanf(" %c%d", &c, &x);
            //printf("%c %d\n", c, x);
            if(c == 'Q')
            {
                printf("%d\n", sum(e[x]) - sum(s[x]));
            }
            else
            {
                if(apple[x])
                {
                    update(e[x], -1);
                    apple[x] = 0;
                }
                else
                {
                    update(e[x], 1);
                    apple[x] = 1;
                }
            }
        }

    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值