以下讲解很有用,via:https://www.cnblogs.com/hsd-/p/6139376.html
【区间查询】
ok 下面利用C[i]数组,求
A数组中前i项的和
举个例子 i=7;
sum[7]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7] ; 前i项和
C[4]=
A[1]+
A[2]+A[3]+A[4]
;
C[6]=A[5]+A[6];
C[7]=A[7];
可以推出:
sum[7]=C[4]+C[6]+C[7];
序号写为二进制: sum[(111)]=
C[(100)]+C[(110)]+C[(111)];
再举个例子 i=5
sum[7]=A[1]+A[2]+A[3]+A[4]+A[5] ; 前i项和
C[4]=
A[1]+
A[2]+A[3]+A[4]
; C[5]=A[5];
可以推出:
sum[5]=C[4]+C[5];
序号写为二进制: sum[(101)]=
C[(100)]+C[(101)];
细细观察二进制 树状数组追其根本就是二进制的应用
int sum(int i) { int res=0; while(i>0) { res+=c[i]; i-=i&(-i); } return res; }
对于i=7 进行演示
7(111)
ans+=C[7]
lowbit(7)=001 7-
lowbit(7)=6(110) ans+=C[6]
lowbit(6)=010 6-lowbit(6)=4(100) ans+=C[4]
lowbit(4)=100 4-lowbit(4)=0(000)
对于i=5 进行演示
5(101)
ans+=C[5]
lowbit(5)=001 5-
lowbit(5)=4(100) ans+=C[4]
lowbit(4)=100 4-lowbit(4)=0(000)
【单点更新】
当我们修改A[]数组中的某一个值时 应当如何更新C[]数组呢?
回想一下 区间查询的过程,再看一下上文中列出的图
void add(int i,int v) { while(i<=n) { c[i]+=v; i+=i&(-i); } }
如图:
当更新A[1]时 需要向上更新C[1] ,C[2],C[4],C[8]
(看图看看这四个C点,会发现其实i+lowbit(i)有一种层次嵌套A[i]的关系,其他就都与你A[i]无关了。)
C[1], C[2], C[4], C[8]
写为二进制
C[(001)],C[(010)],C[(100)],C[(1000)]
1(001) C[1]+=A[1]
lowbit(1)=001 1+lowbit(1)=2(010)
C[2]+=A[1]
lowbit(2)=010 2+lowbit(2)=4(100) C[4]
+=A[1]
lowbit(4)=100 4+lowbit(4)=8(1000) C[8]
+=A[1]
其实你发现没有,A[i]单点还是单点,而C[i]的加入使得区间查询就很方便!
代码中全然没有A数组的存在,完全由C数组来解决问题。
(理解误区:C数组就是A数组的前缀和?错,区间查询才是求前缀和,C[i]是以一种规定好的这个数据结构里的方式来跟A以及其他C建立联系【看图】)
例题【棋子等级】:
大致思路:
用上树状数组的“区间查询”和“单点更新”的模板题。
这道题暴力肯定是能给点分的,但是也是很少的分,因为数据量一大你去遍历肯定要超时。而用上树状数组这个数据结构又可以成功“优化”。
重点在于如何转化成树状数组问题的思路:
我们可以把棋盘的每一个点都看成A[i],默认为0,如果有个棋子则A[i]=1。
左下方肯定是要x和y都比你小的,而题目说了输入的数据按y升序排列,所以结合树状数组“区间查询”的一个流程,我们可以无视y,只看前面的比你的x值小的点的个数啊!即:求A[1]+A[2]+...+A[x] (此时的x因为默认为0,所以不影响题意)
当然,我是边放棋子边立刻计算其等级的,所以统计之后也要“更新单点”,这是为之后的点的统计做准备(即随时更新C[i],即用即有)。
细节注意:树状数组的算法里都是以A[1]和C[1]打头的,所以这个题里面x从0开始,我们就把它视作从1开始,只要把输入的x加1即可。
AC代码:
#include<cstdio> #include<cstring> using namespace std; int c[100001]; int rank[100001]; int n; int getsum(int x){ int sum=0; for(;x;x-=x&(-x)) { sum+=c[x]; } return sum; } void renew(int i,int v){ for(;i<=100000;i+=i&(-i)){ c[i]+=v; } } int main(){ int x,y,number; memset(c,0,sizeof(c)); memset(rank,0,sizeof(rank)); scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d%d",&x,&y); x++; number=getsum(x); rank[number]++; renew(x,1); } for(int i=0;i<n;i++){ printf("%d\n",rank[i]); } return 0; }