poj 3321(树状数组+dfs映射)

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

思路:将一个树映射成树状数组,用dfs 对图中的苹果树进行遍历

dfs遍历顺序i13452
start[i]12345
end[i]54345
start 就是dfs遍历的顺序,也可以理解为dfs调用时入栈的时间,同理,end表示dfs调用结束时即退栈的时间。

这样求1号树叉上的苹果总数就是求从1到5(start[1]=1,start[2]=3,start[3]=4,start[4]=5,start[5]=2)号树叉上的苹果数量,同理3号树叉上的苹果总数就是从2到4号(start[2]=3,start[3]=4,start[4]=5)树叉上的苹果数量

#include<cstdio>
using namespace std;
const int num=100005;
int c[num],n,m,st[num],ed[num],ord=0,head[num],e,vis[num],p[num];
//p记录i的树叉上是否有苹果,初始为有;ord记录遍历顺序,可理解为时间
struct node
{
    int u,next;
}edge[num*2];//邻接表
void init()
{
    int i;
    for(i=0;i<=n;i++)
    {
        head[i]=-1;
        c[i]=0;
        vis[i]=0;
        p[i]=1;
    }
    e=0;
}
void add(int a,int b)
{
    edge[e].u=b;
    edge[e].next=head[a];
    head[a]=e++;
}
int lowbit (int a)
{
    return a&(-a);
}
void updata(int a,int add)
{
    while(a<=n)
    {
        c[a]+=add;
        a+=lowbit(a);
    }
}
int sum(int a)
{
    int ans=0;
    while(a>0)
    {
        ans+=c[a];
        a-=lowbit(a);
    }
    return ans;
}
void dfs(int a)
{
    int i;
    ord++;
    st[a]=ord;
    vis[a]=1;
    for(i=head[a];i!=-1;i=edge[i].next)
    {
        if(vis[edge[i].u]==0)
            dfs(edge[i].u);
    }
    ed[a]=ord;
}
int main()
{
    int i,a,b;
    char s[3];
    //freopen("in.txt","r",stdin);
    scanf("%d",&n);
    init();
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    for(i=1;i<=n;i++)
        updata(i,1);
    dfs(1);
    scanf("%d",&m);
    while(m--)
    {
        scanf("%s",s);
        if(s[0]=='C'&&s[1]=='\0')
        {
            scanf("%d",&a);
            if(p[a]==1)
            {
                updata(st[a],-1);
                p[a]=0;
            }
            else
            {
                updata(st[a],1);
                p[a]=1;
            }
        }
        else if(s[0]=='Q'&&s[1]=='\0')
        {
            scanf("%d",&a);
            printf("%d\n",sum(ed[a])-sum(st[a]-1));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值