树状数组&线段树 的奇妙用法
树状数组只支持单点加,前缀查询,那么不妨通过一些奇妙的途径转化树状数组的性质(因为常数巨小)。
代码和数组定义如下:
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] 加 k k k ,就只需给 l l l 位置加上 k k k , r + 1 r+1 r+1 位置加上 k k k ;单点查询的时候就查询 [ 1 , x ] [1,x] [1,x] 的前缀和即可。
2.区间加,区间求和
从上一个思路出发开始乱搞(虽然线段树秒杀,但是还是有点常数而且结构上可能会有问题不好找)、
我们设 c [ i ] c[i] c[i] 为差分数组,区间求和目标就是 ∑ i = l r ∑ j = 1 i c [ 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 = 1 r ∑ j = 1 i c [ j ] \sum\limits_{i=1}^r\sum\limits_{j=1}^i c[j] i=1∑rj=1∑ic[j] ,交换主体,可得 j j j 能够取 [ 1 , r ] [1,r] [1,r] 之中所有数,而每个 j j j 出现了 r − j + 1 r-j+1 r−j+1 次。
式子就变成了 ∑ j = 1 r c [ 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 = 1 r c [ j ] − ∑ j = 1 r j × 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 ] × j c[j]\times j c[j]×j 即可。