树状数组&线段树 的奇妙用法
树状数组只支持单点加,前缀查询,那么不妨通过一些奇妙的途径转化树状数组的性质(因为常数巨小)。
代码和数组定义如下:
I s[maxn],n;//n 为树状数组值域最大值
void pls(I x,I y){
for(;x<=n;x+=x&-x)s[x]+=y;
}I sum(I x){
I ans=0;
for(;x;x-=x&-x)ans+=s[x];
return ans;
}
1.区间加,单点查询
树状数组维护差分数组即可。区间 [l,r][l,r][l,r] 加 kkk ,就只需给 lll 位置加上 kkk ,r+1r+1r+1 位置加上 kkk ;单点查询的时候就查询 [1,x][1,x][1,x] 的前缀和即可。
2.区间加,区间求和
从上一个思路出发开始乱搞(虽然线段树秒杀,但是还是有点常数而且结构上可能会有问题不好找)、
我们设 c[i]c[i]c[i] 为差分数组,区间求和目标就是 ∑i=lr∑j=1ic[j]\sum\limits_{i=l}^r \sum\limits_{j=1}^i c[j]i=l∑rj=1∑ic[j] 。
发现不太好直接求解,于是转化成 [1,r][1,r][1,r] 的和减去 [1,l−1][1,l-1][1,l−1] 的和。这里以 [1,r][1,r][1,r] 的求和为例。
原式= ∑i=1r∑j=1ic[j]\sum\limits_{i=1}^r\sum\limits_{j=1}^i c[j]i=1∑rj=1∑ic[j] ,交换主体,可得 jjj 能够取 [1,r][1,r][1,r] 之中所有数,而每个 jjj 出现了 r−j+1r-j+1r−j+1 次。
式子就变成了 ∑j=1rc[j]×(r−j+1)\sum\limits_{j=1}^r c[j]\times (r-j+1)j=1∑rc[j]×(r−j+1) 。把可控的部分分离出来,得到 (r+1)×∑j=1rc[j]−∑j=1rj×c[j](r+1)\times \sum\limits_{j=1}^r c[j] -\sum\limits_{j=1}^r j\times c[j](r+1)×j=1∑rc[j]−j=1∑rj×c[j] 。
所以维护两个数组,一个 c[j]c[j]c[j] ,一个 c[j]×jc[j]\times jc[j]×j 即可。