唉,丑陋不堪!
再把最开始得到的结论搬出来:
C1 = A1 C2 = A1+A2 C3 = A3 C4 = A1+A2+A3+A4 C5 = A5 C6 = A5+A6 C7 = A7 C8 = A1+A2+A3+A4+A5+A6+A7+A8还有这个:
C(1) = A1 C(10) = A1+A2 C(11) = A3 C(100) = A1+A2+A3+A4 C(101) = A5 C(110) = A5+A6 C(111) = A7 C(1000) = A1+A2+A3+A4+A5+A6+A7+A8举个栗子,假如我们修改了A[3]的值,C数组中的哪些元素需要修改呢?
通过看图和看图后得到的结论,“显然“就是包含A[3]的C数组的元素,或者说是C[3]和它的”祖先”(反正人们说数都喜欢用祖先这个词),也就是C[3],C[4]和C[8]。
因为你不能给计算机一个xxx.jpg然后让它自动修改需要修改的C数组的元素是不是?所以现在,得到一个递推式来自动处理显得很有必要了。
现在你不用自己找了,因为已经有人帮你找好了。
我们设x下标的二进制从后面往前面看,看到出现一个1时,我们看过的二进制为lowbit(x),如,3的二进制是11,那么lowbit(3)便是1了,又如4的二进制是100,那么lowbit(4)就是100了。
如果我们把3加上lowbit(3),得到4,再把4加上lowbit(4),就得到我们要的8了,这样,就愉快地把要修改的C数组的元素全部找到了。
先把lowbit函数给写了吧:
int lowbit(int x){ return x& (-x); }至于这个lowbit里面是怎么回事,因为涉及到补码什么的,就不讲了,反正也很好记๑乛◡乛๑
不过不能一直加下去吧?边界条件很好找,就是x不会超过n(显然易见的)。
现在把update函数也放出来:
void update(int x,int v){ while(x<=n){//边界条件 c[x]+=v;//将要更新的C数组的元素加上v x+=lowbit(x);//下一个元素 } }之前有一个问题,就是为什么A数组不从0开始,因为lowbit(0)等于0,那么就会永远达不到边界条件,也就是x永远也不会达到n,总之会无限循环下去,就炸了,炸了!
好啦,现在唯一没有讲(che)的是主函数中的这句话了:
cout<<sum(b)-sum(a-1)<<endl;很简单,sum(x)函数是计算序列中第1个数到第x个数的和的函数(绕晕),和前缀和的思想相同,若想求第a个数到第b个数的和,只需要求第1个数到第b个数的和减第1个数到第a-1个数的和即可
那么是时候讲(che)sum函数的构造了!
假如要求序列中第1个数到第7个数的和该怎么弄?看看表就明白了——>C[7]+C[6]+C[4],再拆成二进制C(111)+C(110)+C(100)。那么假如要求序列中第1个数到第6个数的和呢?再看一下表C[6]+C[4],再拆成二进制C(110)+C(100)。
可以看出来,要求第1个数到第x个数的和,只需要从x开始向下递推,然后用一个变量将一堆C[x]加起来,就可以得到第1个数到第x个数的和了,边界条件也是“显然易见”的,那就是x>0或x>=1。
话不多说,上代码:
int sum(int x){ int res=0;//保存一堆C[x]的和的变量 while(x>0){//边界条件 res+=c[x];//加上...... x-=lowbit(x);//下一个 } return res; }这样,这道题就可以AC了!
附上完整代码:
#pragma GCC optimize(3) #include<bits/stdc++.h> using namespace std; static int n,m; static int c[500005]; inline int lowbit(int x){ return x& (-x); } void update(int x,int v){ while(x<=n){ c[x]+=v; x+=lowbit(x); } } int sum(int x){ int res=0; while(x>0){ res+=c[x]; x-=lowbit(x); } return res; } int main(){ cin>>n>>m; for(int i=1;i<=n;i++){ int v; cin>>v; update(i,v); } for(int i=1;i<=m;i++){ int k,a,b; cin>>k>>a>>b; if(k==1) update(a,b); else cout<<sum(b)-sum(a-1)<<endl; } return 0; }请无视我手动开的O3和C++17中全局变量必须加的static......(逃)