如果给你一个数组,让你求某个区间的和,你很自然会想到遍历一遍数组,复杂度是O(n),但是如果有多次询问呢,你也许会想到用前缀数组,通过O(n)的预处理,达到O(1)的查询,但是如果要更新某个元素的值呢,如果用前缀和的思想,每更新一个元素就会更新前缀数组,每次复杂度是O(n),如果有n次更改,复杂度为O(n^2)。有没有更快的呢,这时候树状数组就排上用场了,树状数组可以用来解决动态数组连续和的问题。

树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree):是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。

树状数组大致就长下面这个模样。

那么树状数组到底是怎么把修改和求和都做到O(log(n))呢

当我么求和的时候,c[1]=A[1],c[2]=c[1]+A[2],c[4]=c[2]+c[3]+A[4],当我们更新A[2]的时候,我们只需要更新c[2],c[4],c[8],所以相当与每层我们只操作一个结点,即O(log(n))。

当我们更新的时候c[i]的时候,就从c[i]开始往右走,边走边往上爬。比如我们更新c[2]的时候,我们先更新c[2],然后更新c[4]、c[8]、c[16]。

当我们要求和的时候,我们从c[i]开始往左走,边走边往上爬。比如我们求前7项和,先找到c[7],然后加上c[6],最后加上c[4]。

那我们怎么才能做到这种奇怪的走法呢,

图中灰色结点表示c[i],白色结点表示以i结尾的和,很神奇,灰色结点c[4]=A[1]+A[2]+A[3]+A[4]表示以4结尾的和,我们很好理解,但是为什么灰色结点c[6]表示的表示的只有A[5]+A[6] 我们怎么来控制个数呢,对于c[4],我们相当于从下标4开始,往前数了4个数,对于c[6],我们从下标6开始往前数两个数,有个公式c[i]=A[i-2^k+1]+A[i-2^k+2]......+A[i],其中k表示i的二进制末尾0的个数

      i   2进制      k 下标取值范围  
     1    0001     0   i-2^k+1...i=1...1 c[1]=A[1]
     2   0010     1   i-2^k+1...i=1...2 c[2]=A[1]+A[2]=c[1]+A[2]
     3   0011     0   i-2^k+1...i=3...3 c[3]=A[3]
     4   0100     2   i-2^k+1...i=1...4 c[4]=A[1]+A[2]+A[3]+A[4]=c[2]+c[3]+A[4]
     5   0101     0   i-2^k+1...i=5...5 c[5]=A[5]
     6   0110     1   i-2^k+1...i=5...6 c[6]=A[5]+A[6]=c[5]+A[6]

所以我们求和的时候我们就先找i,然后往前数2^k个数,既然k表示i的二进制末尾0的个数,其实就是前一位对应的权值,比如4的二进制为0100,末尾有2个0,所以2^k=4,从后往前数第一位不为0的出现在第三位,权值为100,即4。

那我们我们怎么计算这个权值呢,lowbit(i)=2^k=x&-x,先给出代码

int lowbit(int x)
{return x&(-x);
}

还是以4为栗子:lowbit(4)=4,4二进制为0100,-4的二进制为4的二进制按位取反然后加1,-4=1100,所以4&(-4)=0100 即为4;

所以c[i]等于以i开始从后往前数i&(-i)个数。

当我们更新c[i]的时候,就从c[i]开始往右走,边走边往上爬。所以i逐渐变大但小于等于n,即更新时的下标变化:i+=lowbit(i)

当我们要求和的时候,我们从c[i]开始往左走,边走边往上爬。所以i逐渐变小但大于0,即求和时下标变化:i-=lowbit(i)

预处理方法:先把A和c数组都清空,然后执行n次add操作,所以时间复杂度为O(nlog(n))。

树状数组主要是用来维护动态数组连续和问题。

HDU 1166 敌兵布阵

题意:有n个营地,每个营地开始都有一定数量的敌军,然后有下面三种命令

(1) Add i j,i和j为正整数,表示第i个营地增加j个人(j不超过30)
(2)Sub i j ,i和j为正整数,表示第i个营地减少j个人(j不超过30);
(3)Query i j ,i和j为正整数,i<=j,表示询问第i到第j个营地的总人数

题解:由于n=50000比较大,而且命令多达40000,所以可以用树状数组来解决,当然也可以用线段树来维护区间和,只不过线段树代码量比较大,所以对于这种单点更新求区间和问题最好使用树状数组,写起来节约时间,也不容易出错。下面分别给出树状数组和线段树的代码。

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=50000+7;
int tree[maxn<<1];       //树状数组
int n;
char s[10];
int lowbit(int x)
{return x&(-x);
}
void add(int x,int d)
{while(x<=n){tree[x]+=d;x+=lowbit(x);}return ;
}
int sum(int x)
{int s=0;while(x>0){s+=tree[x];x-=lowbit(x);}return s;
}
int main()
{int T;scanf("%d",&T);for(int t=1;t<=T;t++){memset(tree,0,sizeof(tree));scanf("%d",&n);for(int i=1;i<=n;i++){int num;scanf("%d",&num);add(i,num);              //开始默认所有的点都是0 每读到一个数就开始往上更新}printf("Case %d:\n",t);int x,y;while(scanf("%s",s)==1&&strcmp(s,"End")){if(!strcmp(s,"Query")){scanf("%d %d",&x,&y);printf("%d\n",sum(y)-sum(x-1));}else if(!strcmp(s,"Add")) {scanf("%d %d",&x,&y);add(x,y);}else {scanf("%d %d",&x,&y);add(x,-y);}}}return 0;
}
#include<cstdio>
#include<cstring>
using namespace std;const int maxn =1e5+7;long long int segtree[maxn<<2],a[maxn];
int n,x,y;
char s[10];void pushup(int now)                   //维护的信息 维护区间和
{segtree[now]=segtree[now<<1]+segtree[(now<<1)|1];return ;
}
void build_tree(int l,int r,int now)
{if(l==r){                   //递归到叶子结点赋值segtree[now]=a[l];return ;          //记得递归一定要renturn 不然就会疯狂递归}int mid=(l+r)>>1;build_tree(l,mid,now<<1);build_tree(mid+1,r,(now<<1)|1);pushup(now);
}long long int query(int ql,int qr,int l,int r,int now)
{if(ql<=l&&r<=qr){     //如果当前区间完全包含在查询区间 就return 当前递归子函数不再向下递归return segtree[now];}long long int ans=0;int mid=(l+r)>>1;if(ql<=mid)ans+=query(ql,qr,l,mid,now<<1);if(qr>mid)ans+=query(ql,qr,mid+1,r,(now<<1)|1);return ans;
}
void update(int pos ,int c,int l,int r,int now)
{if(l==r){segtree[now]+=c;return ;}int mid=(l+r)>>1;if(pos<=mid)update(pos,c,l,mid,now<<1);else update(pos,c,mid+1,r,(now<<1)|1);pushup(now);
}
int main()
{int T;scanf("%d",&T);for(int t=1;t<=T;t++){scanf("%d",&n);memset(segtree,0,sizeof(segtree));for(int i=1;i<=n;i++)scanf("%d",&a[i]);build_tree(1,n,1);printf("Case %d:\n",t);while(scanf("%s",s)==1&&strcmp(s,"End")){if(!strcmp(s,"Query")){scanf("%d %d",&x,&y);printf("%lld\n",query(x,y,1,n,1));}else if(!strcmp(s,"Add")){scanf("%d %d",&x,&y);update(x,y,1,n,1);}else {scanf("%d %d",&x,&y);update(x,-y,1,n,1);}}}return 0;
}

【 HDU 1166】 敌兵布阵 树状数组从0到1相关推荐

  1. hdu 1166 敌兵布阵 树状数组

    敌兵布阵                                                                           Time Limit: 2000/1000 ...

  2. hdu 1166 敌兵布阵 树状数组 模板题

    这题是树状数组入门的一模板题,非常基础,被小白成为"赤裸裸"的入门题,哈哈,一个plus,一个sum全部搞定 #include<stdio.h> #include< ...

  3. HDU 1166 敌兵布阵 树状数组小结(更新)

    树状数组(Binary Indexed Tree(BIT), Fenwick Tree) 是一个查询和修改复杂度都为log(n)的数据结构.主要用于查询任意两位之间的所有 元素之和,但是每次只能修改一 ...

  4. hdu1166敌兵布阵 树状数组裸题

    树状数组裸题 动态更新区间内的点,动态查询区间和 敌兵布阵 ac代码 #include<iostream> #include<algorithm> #include<cs ...

  5. 杭电OJ 敌兵布阵 树状数组

    是一道简单的树状数组的题,,是一道简单题.....题目: C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Der ...

  6. 树状数组板子题之一:hdu 1166 敌兵布阵

    树状数组板子题之一:hdu 1166 敌兵布阵 题目链接:hdu 1166 敌兵布阵 Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手 ...

  7. HDU 1166 敌兵布阵(线段树:点更新,区间求和)

    HDU 1166 敌兵布阵(线段树:点更新,区间求和) http://acm.hdu.edu.cn/showproblem.php?pid=1166 题意: 给你n个整数,然后给你多条命令,每条命令如 ...

  8. hdu 1166 敌兵布阵(单点更新)

    hdu 1166 敌兵布阵(基本操作) 有三种操作:询问区间总和,增加某个兵营的兵的数目,减少某个兵营的兵的数目.实际上也只有两个. 在更新的时候,每到一个区间就把当前区间的sum增加对应的数目,到达 ...

  9. 【树状数组】HDU 1166 敌兵布阵

    敌兵布阵 日常题目胡乱总结 是个裸的树状数组qwq 区间查询+单点修改 字符判断相等那个地方我开始用的单引号 '  ' 直接不通过编译QAQ 第一遍交没有在一组数据结束后清空c数组 WA的让我很懵逼 ...

最新文章

  1. 把委托说透(4):委托与设计模式
  2. bootstrap-datepicker default value
  3. 共享锁+行锁防止其他事务对指定行同时更新
  4. spring boot高性能实现二维码扫码登录(上)——单服务器版
  5. 【华为云技术分享】Entity Framework Core 捕获数据库变动
  6. ABAP SPLITTER技术总结及使用技巧
  7. excel的最大行数和列数
  8. python 小说爬虫_初次尝试python爬虫,爬取小说网站的小说。
  9. 【量化交易】 量化因子 情绪类因子计算
  10. 小米集团回应造车;移动公布1月份运营数据;中天科技拟回购公司股份;工信部发布春节流量数据...
  11. 基于Python实现的PageRank算法
  12. 目前服务器的操作系统有哪些?
  13. 网络ioctl实践1:获取网卡的MAC和IP
  14. 安卓系统应用程序开发!华为Android面试真题解析,内含福利
  15. C++ 常用的STL库
  16. 如何用 CSS 和原生 JS 创作一个展示苹果设备的交互动画
  17. ssm+jsp计算机毕业设计疫情救灾物资管理系统6pdz4(程序+lw+源码+远程部署)
  18. 网络监听技术与常规测试方法
  19. 3Q大战现高潮,360 推出Android 3Q IM即时通讯,岁末年初3Q大战惊现高潮
  20. ASP.NET Core 修改开源协议为MIT,.NET全平台 MIT协议开源了

热门文章

  1. Chrome浏览器安装JsonView插件
  2. ERPS环网络端口角色
  3. signature=bb62aa304c060f1aa02747ce12745e99,潘通色卡颜色代码及参考色对照表
  4. 【024】中国色–那些流淌在历史中的中国色彩
  5. SSH免密登陆节点互信
  6. Android4.4 内置应用默认打开无障碍服务
  7. python matplotlib pyplot方法_Matplotlib.pyplot 常用方法
  8. SS2022 信号与系统期中练习题
  9. github精选-一款高颜值的Redis客户端
  10. 快速远程桌面公司内网电脑,远程OA/ERP/BUG/NAS【无需公网IP】