(树状数组)hdu1166 敌兵布阵

今天学习了树状数组,看了能解决的问题之后觉得跟线段树非常像,不过后来了解到线段树的用途其实很广泛,如单点更新、区间更新、区间查询、最值查询等问题,都能够高效的解决。而树状数组则是一种空间占用率很低、且算法效率高(O(logn))的一种数据结构。并且,代码很简洁,只用短短几行代码便完成了所有的操作。

首先是lowbit操作——树状数组的核心。这个操作的作用是把一个二进制数变为只保留最低位的1后形成的数,如110010——变成10。实现方法是,由计算机补码运算的原理,对一个数取相反数相当于逐位取反再加1,再用这个数本身和他的相反数做逐位与(&)操作,即得到最后一位1,具体代码如下:

int lowbit(int k) {//树状数组基本操作,取一个数(二进制)最低位的1
    return k & (-k);//(由计算机补码原理,一个数取负相当于这个数逐位取反再加1
}

以下是更新、求和操作:

单点更新:

void update(int pos, int num) {//从pos到n,更新节点的值
    while(pos <= n) {
        a[pos] += num;//更新
        pos += lowbit(pos);//向上更新受到影响的祖先结点
    }
    return;
}

求和:

int sum(int end) {//求一段区间的和
    int sum = 0;
    while(end > 0) {
        sum += a[end];
        end -= lowbit(end);
    }
    return sum;
}

有了这些基本操作之后,用这道题,hdu1166——敌兵布阵,练了一下手。昨天用线段树做出了它,现在改用树状数组实现,代码简洁易懂。

题意:一个数组a[n],有如下三个操作:

(1) Add i j,i和j为正整数,表示第i个数增加j

(2)Sub i j ,i和j为正整数,表示第i个数减少j(j不超过30);

(3)Query i j ,i和j为正整数,i<=j,表示询问ai到aj的和;

简单的树状数组应用,加减操作可以通过单点更新来实现,查询区间和可由sum(b)-sum(a-1)得到。具体代码如下:

#include<bits/stdc++.h>
typedef long long ll;

const int maxn = 50010;

int t, n;
int a[maxn];

int lowbit(int k) {//树状数组基本操作,取一个数(二进制)最低位的1
    return k & (-k);//(由计算机补码原理,一个数取负相当于这个数逐位取反再加1
}

void update(int pos, int num) {//从pos到n,更新节点的值
    while(pos <= n) {
        a[pos] += num;//更新
        pos += lowbit(pos);//向上更新受到影响的祖先结点
    }
    return;
}

int sum(int end) {//求一段区间的和
    int sum = 0;
    while(end > 0) {
        sum += a[end];
        end -= lowbit(end);
    }
    return sum;
}

int main() {
    while(~scanf("%d", &t)) {
        for(int cas = 1;cas <= t;cas++) {
            scanf("%d", &n);
            int m;
            memset(a, 0, sizeof(a));
            for(int i = 1;i <= n;i++) {
                scanf("%d", &m);
                update(i, m);//每输一个数,更新一次,建立树状数组
            }
            char str[10];
            printf("Case %d:\n", cas);
            while(scanf("%s", str), strcmp(str, "End")) {
                int a1, a2;
                if(strcmp(str, "Add") == 0) {
                    scanf("%d %d", &a1, &a2);
                    update(a1, a2);//向上更新
                }
                else if(strcmp(str, "Sub") == 0) {
                    scanf("%d %d", &a1, &a2);
                    update(a1, -a2);//向上更新
                }
                else if(strcmp(str, "Query") == 0) {
                    scanf("%d %d", &a1, &a2);
                    printf("%d\n", sum(a2) - sum(a1-1));//b到a-1即为a到b段的和
                }
            }
        }
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了。A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况。由于采取了某种先进的监测手段,所以每个工兵营地的人数C国都掌握的一清二楚,每个工兵营地的人数都有可能发生变动,可能增加或减少若干人手,但这些都逃不过C国的监视。 中央情报局要研究敌人究竟演习什么战术,所以Tidy要随时向Derek汇报某一段连续的工兵营地一共有多少人,例如Derek问:“Tidy,马上汇报第3个营地到第10个营地共有多少人!”Tidy就要马上开始计算这一段的总人数并汇报。但敌兵营地的人数经常变动,而Derek每次询问的段都不一样,所以Tidy不得不每次都一个一个营地的去数,很快就精疲力尽了,Derek对Tidy的计算速度越来越不满:"你个死肥仔,算得这么慢,我炒你鱿鱼!”Tidy想:“你自己来算算看,这可真是一项累人的工作!我恨不得你炒我鱿鱼呢!”无奈之下,Tidy只好打电话向计算机专家Windbreaker求救,Windbreaker说:“死肥仔,叫你平时做多点acm题和看多点算法书,现在尝到苦果了吧!”Tidy说:"我知错了。。。"但Windbreaker已经挂掉电话了。Tidy很苦恼,这么算他真的会崩溃的,聪明的读者,你能写个程序帮他完成这项工作吗?不过如果你的程序效率不够高的话,Tidy还是会受到Derek的责骂的.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值