POJ 3321 - Apple Tree

题目描述

Apple Tree

解法:树状数组

首先使用树状数组是很明确的,准确来说是一个PUIQ模型(单点更新,区域查询)。

但是现在我们的输入不是一个数组了,而是一棵树,怎么办呢?

对一棵树进行深搜,然后将深搜的顺序重新标上号,然后放到一个数组中(即树状数组的线性表部分 a [ . . . ] a[... ] a[...]),记下每一个分支的起始点和终结点,然后就可以用树状数组了。

举个栗子吧,
在这里插入图片描述
那么当我们深搜的时候就可以在下面标上:

1 4 6 5 7 2 3 (广义表里面的东西,深搜结果)

1 2 3 4 5 6 7( a [ . . . ] a[... ] a[...]数组我们求和要用的)

s t a r t [ 1 ] = 1     e n d [ 1 ] = 7 start[1] = 1 \ \ \ end[1]=7 start[1]=1   end[1]=7代表1以上的树枝为1-7

s t a r t [ 2 ] = 6     e n d [ 2 ] = 7 start[2]= 6 \ \ \ end[2] = 7 start[2]=6   end[2]=7代表2以上的树枝是 3

找到每一个树枝在 a [ . . . ] a[... ] a[...]数组中的位置,这样我们就可以计算啦,当然实际编程中并不需要 a [ . . . ] a[... ] a[...]数组,只需要直接更新树状部分。

下面解释一下编码中一些问题,

编码中的树结构其实是有向图的邻接表,这里仅是根据题意采用树的说法。而dfs中没有显示的 v i s i t e d [ . . . ] visited[...] visited[...]数组,是因为我们将访问过的结点都赋予了新的id编号,该编号从1开始。根据题目意思,查询树枝 x x x上方子树中的苹果数量是包括 x x x上的苹果的(如果存在),因此在区域查询中应为 ( s t a r t [ x ] − 1 , e n d [ x ] ) (start[x]-1, end[x]) (start[x]1,end[x]).

#include <iostream>

using namespace std;

int n, m, s, t, x, id = 0;
int c[100001], start[100001], _end[100001];
char cmd;

struct TreeNode{
    int forkno;
    TreeNode* next;
}tree[100001];

inline int min(int x, int y){return x<y?x:y;}

inline int lowbit(int x){return x&(-x);}

void add(int x, int v)
{
    for(int i=x;i<=n;i+=lowbit(i))
        c[i] += v;
}

int sum(int x)
{
    int s = 0;
    for(int i=x;i;i-=lowbit(i))
        s += c[i];
    return s;
}

void dfs(int pos)
{
    start[pos] = ++id;
    add(id, 1);
    TreeNode* p = tree[pos].next;
    while(p)
    {
        if(start[p->forkno]==0) dfs(p->forkno);
        p = p->next;
    }
    _end[pos] = id;
}

int main()
{
    scanf("%d", &n);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d", &s, &t);
        TreeNode* p = new TreeNode;
        p->forkno = t;
        p->next = tree[s].next;
        tree[s].next = p;

        TreeNode* q = new TreeNode;
        q->forkno = s;
        q->next = tree[t].next;
        tree[t].next = q;
    }
    dfs(1);
    scanf("%d", &m);
    for(int i=0;i<m;i++)
    {
        getchar();
        scanf("%c%d", &cmd, &x);
        if(cmd=='C')
        {
            int _sum = sum(start[x])-sum(start[x]-1);
            if(_sum) add(start[x], -1);
            else add(start[x], 1);
        }
        else
        {
            int _sum = sum(_end[x]) - sum(start[x]-1);
            printf("%d\n", _sum);
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值