POJ 3321 Apple Tree DFS + 树状数组

题意:一棵树树上有N个分叉点,分个分叉点上有一个苹果或者没有苹果,现在想知道以某一个分叉点为根节点的子树有多少苹果。

从题目的叙述中可以知道给出的数据并不是一棵二叉树,所以一开始往并查集的方向做,每次更新都要从当前节点一直更新到根节点。

当这棵树是一条单链的时候,这种做法的耗时就显得有点丧心病狂了。。。。

下面说正解。

此题其实为区间查询题,可是问题在于线段书只能查询一段连续的区间内的值,对于离散的点线段树就丝毫无用武之地了。

下面DFS这个大杀器就要粉墨登场了。

若对点重新用DFS时被访问的先后顺序进行编号,即最先被访问的编号为1,接下来被访问的为2,以此类推,则会发现一棵树上的点的编

号从根节点一直连续到最后一个被访问的叶子结点。

接下来的问题就可以交给树状数组来做了,不再赘述。

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>

#pragma comment(linker, "/STACK:1024000000");
#define LL long long int

using namespace std;

const int MAXN = 1000010;

int id[MAXN];

int st[MAXN*4];

bool mark[MAXN];

struct N
{
    int data;
    N *next;
}*head[MAXN];

N *creat()
{
    N *p = (N *)malloc(sizeof(N));
    p->data = 0;
    p->next = NULL;
    return p;
}

void link(int son,int father)
{
    head[son]->data ++;
    N *p = creat();
    p->data = son;
    p->next = head[father]->next;
    head[father]->next = p;
}

int Time;

int dfs(int root)
{
    id[root] = Time++;

    if(head[root]->next == NULL)
    {
        head[root]->data = Time-1;
        return Time-1;
    }

    int temp,Max = -1;

    for(N *p = head[root]->next; p != NULL ; p = p->next)
    {
        if( (temp = dfs(p->data)) > Max)
            Max = temp;
    }

    head[root]->data = Max;

    return Max;
}

void Init(int site,int l,int r)
{
    st[site] = r-l+1;

    if(l == r)
    {
        st[site] = 1;
        return ;
    }

    int mid = (l+r)>>1;

    Init(site<<1,l,mid);
    Init(site<<1|1,mid+1,r);
}

int query(int site,int L ,int R,int l,int r)
{
    if(L == l && R == r)
    {
        return st[site];
    }

    int mid = (L + R)>>1;

    if(r <= mid)
    {
        return query(site<<1,L,mid,l,r);
    }
    else if(mid  < l)
    {
        return query(site<<1|1,mid+1,R,l,r);
    }
    else
    {
        return query(site<<1,L,mid,l,mid) + query(site<<1|1,mid+1,R,mid+1,r);
    }
}

void updata(int site,int l,int r,int m)
{
    if(mark[m] == false)
    {
        st[site]--;
    }
    else
    {
        st[site]++;
    }

    if(l == r && r == m)
    {
        if(mark[m] == false)
        {
            mark[m] = true;
            return ;
        }
        else
        {
            mark[m] = false;
            return ;
        }
    }

    int mid = (l+r)>>1;

    if(m <= mid)
    {
        updata(site<<1,l,mid,m);
    }
    else
    {
        updata(site<<1|1,mid+1,r,m);
    }
}

int main()
{
    int n,i,u,v,m;
    char order;
    scanf("%d",&n);

    for(i = 1; i <= n; ++i)
    {
        head[i] = creat();
    }

    memset(mark,false,sizeof(bool)*(n+2));

    for(i = 1; i < n; ++i)
    {
        scanf("%d %d",&u,&v);
        if(head[u]->data == 0 && u != 1)
        {
            link(u,v);
        }
        else if(head[v]->data == 0 && v != 1)
        {
            link(v,u);
        }
    }
    Time = 0;

    dfs(1);

    Init(1,0,Time-1);

    scanf("%d",&m);

    while(m--)
    {
        scanf("%*c%c %d",&order,&v);

        if(order == 'Q')
        {
            printf("%d\n",query(1,0,Time-1,id[v],head[v]->data));
        }
        else
            updata(1,0,Time-1,id[v]);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值