POJ 3321 AppleTree (树状数组)

       一个星期前,被USACO上的一个题卡住了,我一看就是一道二维线段树的题目, 就和POJ上1177 Picture差不多。所以就意味着我要重新学习一下线段树, 再研究Picture这个题(上次研究过一段时间,最后放弃了),就这么东找西找就 找到了这个题,试图用线段树做这个题,但是无从下手,不知道如何把题目中的 n-1对u和v转换成数,百度一下找人家的解题报告,看了很多,还是不懂, 还是那句话:看别人的代码学习算法很难受啊。 后来发现大家都用的一个叫做“树状数组”的东西,于是就研究了一下,发现“树状数组” 确实是个好东西。最后在这里看懂了用“树状数组”解本题的方法:

 http://blog.csdn.net/logic_nut/archive/2009/09/22/4579215.aspx 

再把自己的代码修改,神啦,终于A了,虽然时间是1000+M。我也不管了,


/**
    刚开始被题目中的图片误导了,以为只是二叉树,写出了代码,WA了,
    后来才发现,原来是多叉树,这就不好处理了,于是就用了树的
    "孩子链存储方式",好不容易修改完了,提交,悲剧的TLE。。。
    没办法,还是看看大家都在讨论的“树状数组”。看别人的代码来学习
    一种算法,真是一件痛苦的事情啊。。。。。
    最后终于懂了“树状数组”是一个什么东东。
    而对于这题,难点在于:怎么把题目中的树映射成树状数组。。
**/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct CH
{
    int idx;
    struct CH *nxt;
} CHILD;

typedef struct N
{
    int   index;   //在树状数组中的编号
    int   minIdx;  //其所有子结点中的最小编号
    CHILD *chld;   //孩子链指针
} NODE;
/*   使用孩子链的存储方式  */

NODE fork[100005];
char vst[100005];
char apple[100005];
int  n, cnt;
int  treeArr[100005];

/*  认为y是x的孩子结点,
    在fork[x].chld中前插一个结点,
    结点的idx值为y        */
void addChild(int x, int y)
{
    CHILD *p;
    p = (CHILD*)malloc(sizeof(CHILD));
    p->idx = y;
    p->nxt = fork[x].chld;
    fork[x].chld = p;
}

/*   后序遍历树,更新各个结点在树状数组中的
     编号index,和其子结点中的最小编号minIdx*/
void DFS(int r)
{
    CHILD *p;
    vst[r] = 1;
    fork[r].minIdx = cnt; //这个的原因自己领会
    p = fork[r].chld;
    while (p)
    {
        if (vst[p->idx] == 0) //如果p->idx已经被访问过了,就说明p->idx不是r的孩子,而是r的父节点
            DFS(p->idx);
        p = p->nxt;
    }
    fork[r].index = cnt++; //最后遍历根结点
}

/* 以下这三个函数都是涉及树状数组的 */
int lowbit(int i)
{
    return i&(-i);
}

int Sum(int i)
{
    int sum = 0;
    while(i > 0)
    {
        sum += treeArr[i];
        i = i-lowbit(i);
    }
    return sum;
}

void Change(int i)
{
    int t = 1;
    if(apple[i]) t = -1;
    apple[i] += t;
    while(i <= n)
    {
        treeArr[i] += t;
        i = i+lowbit(i);
    }
}
/* ******************************* */

int main()
{
    int  m, i, j, u, v;
    int  fk, sum;
    char ch;
    memset(fork, 0, sizeof(fork));
    memset(apple, 1, sizeof(apple)); //初始每个枝上都有苹果

    scanf("%d", &n);
    for (i=1; i<n; i++)
    {
        scanf("%d %d", &u, &v);
        addChild(u, v);   //v是u的孩子
        addChild(v, u);   //u是v的孩子
    }
    cnt = 1;
    DFS(1);  //后序遍历
    for (i=1; i<=n; i++)
        treeArr[i] = lowbit(i); //建立树状数组treeArr[]
    scanf("%d", &m);
    while (m--)
    {
        getchar();
        scanf("%c %d", &ch, &fk);
        if ('C' == ch)
            Change(fork[fk].index);
        else
            printf("%d/n", Sum(fork[fk].index)-Sum(fork[fk].minIdx-1));
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值