POJ3321——树状数组_POJ树状数组初探

本文出自:http://blog.csdn.net/svitter


树状数组用于处理求区间内的值和改变单个元素的值,要注意树状数组从数组下标1开始。


基础算法:

获取全部的lowbit值,防止重复计算。

void getLowbit()
{
    for(int i = 0; i < 1000; i++)
        lowbit[i] = i & (-i);
}

求区间和1~i:

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

改变一个元素i数值:

void Change(int i, int inc)
{
	while(i <= n)
	{
		C[i] += inc;
		i = i + lowbit[i];
	}
}


Cn的求法:

sum(n) - sum(n - lowbit(n));


题目:POJ3321

题意:一颗苹果树,树上有多个苹果,一个分支一个苹果,求一个节点以上苹果的个数。操作有添加和删除苹果。(如果原本有苹果,则添加,没有则删除。一开始全部有苹果)。

测试数据:第一行为一个n,代表分支个数。随后输入分支关系。(邻接表)第二行为一个m,代表操作个数。随后输入Q或者C加一个数字,代表求和或者对树进行删除或者添加操作。

思路:考虑是稀疏图使用邻接表存存储。STL_稀疏图,树_使用vector邻接表存储。求段间和,只改变其中一个元素,可以使用树状数组。以根为1,向上节点为另一边。使用深搜统计苹果数量。

C[i]就是Sum(i) - Sum(i - lowbit[i]) 即为 i - (i - lowbit[i]); C[ time ] ;

Q: 使用start[n]和end[n]来存储访问时间,使用( end[n] - start[n] + 1 )/ 2 求出最后结果(除了选择节点的苹果,其他的苹果都走了两遍)。

C: 更改与树状数组的modify结合,更改apple节点。Query函数求出的不是苹果的值。

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

using namespace std;

#define MY_MAX 220000

typedef vector<int> VCT_INT;
vector <VCT_INT> G(MY_MAX/2);


int C[MY_MAX];
int lowbit[MY_MAX];

bool HasApple[MY_MAX];

int start[MY_MAX];
int end[MY_MAX];
int nCount = 0;

void DFS(int v)
{
    start[v] = ++ nCount;
    for(int i = 0; i < G[v].size(); i++)
        DFS(G[v][i]);
    end[v] = ++ nCount;
}


int QuerySum(int p)
{
    int nSum = 0;
    while(p > 0)
    {
        nSum += C[p];
        p -= lowbit[p];
    }
    return nSum;
}

void Modify(int p, int val)
{
    while(p <= nCount)
    {
        C[p] += val;
        p += lowbit[p];
    }
}

void getLowbit()
{
    for(int i = 1; i < MY_MAX; i++)
        lowbit[i] = i & (-i);
}


int main()
{
    int n, m;
    scanf("%d", &n);
    int x, y, i, j ,k;
    int a, b;
    getLowbit();
    //build map
    for(i = 0; i < n - 1; i++)
    {
        scanf("%d%d", &a, &b); 
        G[a].push_back(b); 
    } 
    nCount = 0; DFS(1); 
    for(i = 1; i <= n; i++)
        HasApple[i] = 1;

    for(i = 1; i <= nCount; i++)
        C[i] = i - (i - lowbit[i]);

    scanf("%d", &m);

    while(m--)
    {
        char cmd[10];
        scanf("%s%d", cmd, &a);
        if(cmd[0]== 'C')
        {    if(HasApple[a])
            {
                Modify(start[a], -1);
                Modify(end[a], -1);
                HasApple[a] = 0;
            }
            else
            {
                Modify(start[a], 1);
                Modify(end[a], 1);
                HasApple[a] = 1;
            }
        }
        else
        {
            int t1 = QuerySum(end[a]);
            int t2 = QuerySum(start[a]-1);
            printf("%d\n", (t1-t2)/2);
        }
    }

    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值