POJ3321 Apple Tree

树状数组+DFS  

1.    题意:有一颗苹果树,每个每个节点都能长1个苹果,初始时,苹果是满的,每次可能读取的事件,

a)     询问在某个节点为根的子树的苹果总

b)     摘取某节点的苹果

c)     在某个某个节点长出苹果

2.    首先自己写的时候想直接用父子关系来更新每个节点的苹果,结果超时了,后来看了题解才知道还能转化为树状数组,原来树状数组的使用也是很灵活的

3.    首先的话就是把原来树上的父子关系转化为一个可以用树状数组来处理得关系,又因为刚好深度遍历在同一子树上的所有节点都是连在一起的,所以就可以先求出节点的(前序遍历)深度遍历顺序,而后面再求某个子树的和就等于右节点的sum-左节点的sum


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

#define N 100010

using namespace std;


struct Edge
{
    int next;
    int v;
}g[N*2];
struct Node
{
    int l,r;
    bool apple;
}tree[N];
int head[N];
int n,coun,m,top;
int c[N];
int index[N];

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

void addedge(int x,int y)
{
    g[coun].next=head[x];
    g[coun].v=y;
    head[x]=coun++;

    g[coun].next=head[y];
    g[coun].v=x;
    head[y]=coun++;
}
void DFS(int pos,int last)
{
    int ret=1;
    tree[pos].l=top++;
    index[pos]=top;
    for(int p=head[pos];p;p=g[p].next)
    {
        if(g[p].v != last)
        {
            DFS(g[p].v,pos);
        }
    }
    tree[pos].r=top;
    tree[pos].apple=true;
}

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

int getsum(int p)
{
    int ret=0;
    for(int i=p;i>0;i-=lowbit(i))
    {
        ret+=c[i];
    }
    return ret;
}
int main()
{
    //freopen("input.txt","r",stdin);
    memset(head,0,sizeof(head));
    scanf("%d",&n);
    int X,Y;
    char ch;
    coun=1;
    for(int i=1;i<n;++i)
    {
        scanf("%d%d",&X,&Y);
        addedge(X,Y);
    }
    top=0;
    DFS(1,0);
    //init sum array
    memset(c,0,sizeof(c));
    for(int i=1;i<=n;++i)
    {
        insert(i,1);
    }
    scanf("%d",&m);
    for(int i=0;i<m;++i)
    {
        scanf(" %c %d",&ch,&X);
        if(ch == 'Q')
        {
            printf("%d\n",getsum(tree[X].r)-getsum(tree[X].l));
        }
        else
        {
            if(tree[X].apple)
            {
                insert(index[X],-1);
            }
            else
            {
                insert(index[X],1);
            }
            tree[X].apple=!tree[X].apple;
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值