hdu1166 树状数组模板:单点更新,区间求和(区间查询)

hdu1166
树状数组:单点更新,区间求和(区间查询)
lowbit(k)就是把k的二进制的高位1全部清空,只留下最低位的1和后面的0
t = lowbot(k) 就是求出t = 2的x次方 ,并且 t <= k;
如:lowbit(5) = 4; lowbit(7) = 4;lowbit(2) = 2;
Lowbit(k) = k &(-k);具体原理涉及补码.
对于一个数,我们有原码,反码,补码三种表现形式
1、 原码:原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值.
a) [+1]原 = 0000 0001
b) [-1]原 = 1000 0001
2、反码:正数的反码是其原码,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
c) [+1] = [00000001]原 = [00000001]反
d) [-1] = [10000001]原 = [11111110]反
3、补码:正数的补码就是其原码也是其反码,负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
e) [+1] = [00000001]原 = [00000001]反 = [00000001]补
f) [-1] = [10000001]原 = [11111110]反 = [11111111]补
在计算机中,减去一个正数,被看做是加上一个负数,就可以只计算加法,那么使用补码可以使得计算过程简单,并且符号位也直接参与运算,就不用特判符号位。
计算十进制的表达式: 1-1=0
= 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
学树状数组必看的图片1学树状数组必看的图片2
lowbit(k)就是把k的二进制的高位1全部清空,只留下最低位的1和后面的0,
比如10的二进制是1010,则lowbit(k) = lowbit(1010)
= 0010(2进制) = 2(十进制),实现方法:lowbit(k)=k&-k。
有了lowbit(k),我们就可以方便的将a和c数组联系在一起了,c[k]表示从a[k]开始往前连续lowbit(k)个数的和
比如sum[k] = 从c[k]开始往前连续c[k - lowbit(k)]的和 sum[6]= c[6] + c[4](k需要不断更新)
此题hdu1166
树状数组:单点更新,区间求和(区间查询)、

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 50000 + 5;
int n,t,m,a[maxn],c[maxn],ta,tb;
char s[500];
int lowbit(int x)
{
    return x & (-x);
}
void add(int pos,int x)//单点更新
//如:更新a[6]需要对应更新c[6],c[8],c[16]...
{
    for(int i = pos; i <= n; i += lowbit(i))
    {
        c[i] += x;
    }
}
int query(int x)
//如:查询a[1]...a[6]需计算c[6]+c[4]
{
    int ans = 0;
    for(int i = x; i >= 1; i -= lowbit(i))
    {
        ans += c[i];
    }
    return ans;
}
int main()
{
    scanf("%d",&m);
    int t = m;
    while(t--)
    {
        memset(c,0,sizeof(c));
        printf("Case %d:\n",m - t);
        scanf("%d",&n);
        for(int i = 1; i <= n; ++i)     scanf("%d",&a[i]);
        for(int i = 1; i <= n; ++i)     add(i,a[i]);
        while(1)
        {
            scanf("%s",s);
            if(s[0] == 'E') break;
            scanf("%d%d",&ta,&tb);
            if(s[0] == 'A') add(ta,tb);
            if(s[0] == 'S') add(ta,-tb);
            if(s[0] == 'Q') 
            {
                int x = query(ta - 1),y = query(tb);
                printf("%d\n",y - x);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值