poj 3321 Apple Tree 线段树

题目意思很简单: 给你一颗n个节点的树,每个节点开始有一个苹果,然后m次修改,每次修改使得某个节点的苹果改变,有变成没有,没有变成有。询问的是某个节点及其子树的苹果数目。
    刚开始直接水,直接建树模拟,tle了,因为如果树的高度非常大的话,每次修改的复杂度是o(n)的。
    然后改用线段树,每次询问一个区间的苹果数目,修改的是一个点的值。只要每科子树的节点编号是连续的就可以用线段树做了。这个很容易做到,按dfs建树的顺序给节点编号,那么每棵子树的编号就都是连续的了。 建树的时候顺便求出每个节点的儿子个数,那么查询的时候其区间就是(num[t],num[t]+son[t]-1)。
    这个题目感觉还是挺不错的!
#include <iostream>
#include <stdio.h>
#include <string.h>
#define ls t<<1
#define rs t<<1|1
#define midt (tr[t].l+tr[t].r)>>1
using namespace std;
const int maxn=100001+100;
int head[maxn],lon;
int a[maxn];
struct
{
    int to,next;
}e[maxn<<2];
int edgemake(int from,int to)
{
    e[++lon].to=to;
    e[lon].next=head[from];
    head[from]=lon;
    return(0);
}
int n;

int text[maxn],son[maxn],num[maxn];
int numn;
int dfs(int t)
{
    num[t]=++numn;
    son[t]=1;
    for(int k=head[t];k!=-1;k=e[k].next)
    {
        int u=e[k].to;
        if(!text[u])
        {
            text[u]=1;
            dfs(u);
            text[u]=0;
            son[t]+=son[u];
        }
    }
    return(0);
}

struct
{
    int l,r;
    int sum;
}tr[maxn<<2];

int maketree(int t,int l,int r)
{
    tr[t].l=l;
    tr[t].r=r;
    tr[t].sum=1;
    if(l==r)
    return(0);
    int mid=midt;
    maketree(ls,l,mid);
    maketree(rs,mid+1,r);
    tr[t].sum=tr[ls].sum+tr[rs].sum;
}

int modify(int t,int txt,int tmp)
{
    if(tr[t].l==tmp&&tr[t].r==tmp)
    {
        tr[t].sum+=txt;
        return(0);
    }
    int mid=midt;
    if(tmp<=mid)
    modify(ls,txt,tmp);
    else if(mid+1<=tmp)
    modify(rs,txt,tmp);
    tr[t].sum+=txt;
}

int query(int t,int l,int r)
{
    if(tr[t].l>=l&&tr[t].r<=r) return(tr[t].sum);
    int ret=0;
    int mid=midt;
    if(l<=mid) ret+=query(ls,l,r);
    if(r>=mid+1) ret+=query(rs,l,r);
    return(ret);
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        lon=0;
        memset(head,-1,sizeof(head));
        for(int i=1;i<n;i++)
        {
            int from,to;
            scanf("%d %d",&from,&to);
            edgemake(from,to);
            edgemake(to,from);
        }
        memset(text,0,sizeof(text));
        memset(son,0,sizeof(son));
        text[1]=1;
        numn=0;
        dfs(1);

        maketree(1,1,n);
        int m;
        scanf("%d",&m);
        memset(a,0,sizeof(a));
        char tmp[10];
        for(int i=1;i<=m;i++)
        {
            int txt;
            scanf("%s %d",tmp,&txt);
            if(tmp[0]=='Q')
            {
                int ans=query(1,num[txt],num[txt]+son[txt]-1);
                printf("%d\n",ans);
            }
            else
            {
                a[txt]^=1;
                if(a[txt]==1)
                modify(1,-1,num[txt]);
                else
                modify(1,1,num[txt]);
            }
        }
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值