刚刚学了树状数组的区间加法和区间求和操作,就用来水掉这题了
本篇适合学会树状数组的人群
前置芝士:
【分析】
学过树状数组的人都知道,我们对于一个数组,进行处理后,就可以在 \(O(\log n)\) 的时间内进行单点修改和区间求和
假设对于数组 \(a_n\) ,我们用树状数组的方法处理后,就可以单点修改任意 \(a_i\) 的值,或者求 \(\displaystyle \sum_{i=l}^r a_n\)
如果我们差分一下 \(a_n\) ,设 \(c_n=a_n-a_{n-1}\) ,那就可以使得树状数组区间修改和单点查询 \(\displaystyle a_n=\sum_{i=1}^n c_i\)
在差分的基础下,我们如果需要求区间和,则先相同地:
\(\displaystyle \sum_{i=l}^ra_i=\sum_{i=1}^ra_i-\sum_{i=1}^{l-1}a_i\)
所以,在此基础上,我们只需要求解 \(\displaystyle \sum_{i=1}^k a_i\) 即可求解区间求和问题了
我们变型这个式子:
\(\displaystyle \sum_{i=1}^k a_i=\sum_{i=1}^k\sum_{j=1}^ic_i=\sum_{i=1}^k(k+1-i)c_i\)
这玩意儿没办法直接求,所以我们把它分开来:
\(\displaystyle \sum_{i=1}^k a_i=(k+1)\sum_{i=1}^k c_i-\sum_{i=1}^k ic_i\)
这样,就能分开来,用树状数组实现求和了。
因此,我们只需要实现 \(c_n\) 和 \(nc_n\) 的树状数组,即可以实现区间求和
即前者: \(c_n\) 差分树状数组的区间和,乘上 \((k+1)\) ,减去后者: \(nc_n\) 差分树状数组的区间和
当然,我们还应考虑修改对吧
假设修改区间 \([l,r]\) 都加上 \(a\)
首先, \(c_n\) 的修改肯定是没问题的:\(c_l+=a,c_{r+1}+=-a\)
分解为差分树状数组单点修改 \(c_l\) 和 \(c_{r+1}\)
而我们很清楚,假设 \(c_i\) 转变为 \((c_i+a)\)
则有 \(ic_i\) 转变为 \(i(c_i+a)=ic_i+ia\)
也就是说,\(ic_i+=ia\) ,而 \(ic_i\) 的增加对本身累加了 \(ic_i\) 的贡献都是增加 \(ia\) 的
所以只要在修改 \(c_i+=a\) 的时候,顺便修改 \(ic_i+=ia\) 即可
那本蒟蒻就放 我码风极丑的 代码了:
#include<cstdio>
using namespace std;
#define f(a,b,c,d) for(register int a=b,c=d;a<=c;a++)
#define g(a,b,c,d) for(register int a=b,c=d;a>=c;a--)
#define LOCAL
typedef int i32;
typedef unsigned int u32;
typedef long long int i64;
typedef unsigned long long int u64;
const i32 MAXN=1e5+10;
typedef i64 ar[MAXN];
//一堆条件反射的定义
namespace HABIT{//读入输出优化而已,直接跳过看正文即可
#ifdef LOCAL
inline char gc() { return getchar(); }
#else
inline char gc() {
static char s[1<<20|1]={0},*p1=s,*p2=s;
return (p1==p2)&&(p2=(p1=s)+fread(s,1,1<<20,stdin),p1==p2)?EOF:*(p1++);
}
#endif
inline i32 read(){
register i32 ans=0;register char c=gc();register bool neg=0;
while(c<48||c>57) neg^=!(c^'-'),c=gc();
while(c>=48&&c<=57) ans=(ans<<3)+(ans<<1)+(c^48),c=gc();
return neg?-ans:ans;
}
char Output_Ans[1<<20|1],*Output_Cur=Output_Ans;
inline void output() { Output_Cur-=fwrite(Output_Ans,1,Output_Cur-Output_Ans,stdout); }
inline void print(char c){ if(Output_Cur-Output_Ans+1>>20) output(); *(Output_Cur++)=c; }
inline void print(char *s) { while(*s) print( *(s++) ); }
inline void print(u64 x){
if(!x) { print('0'); return ; }
char buf[30]={0},*p=buf+28;
while(x) *(p--)=x%10+48,x/=10;
print(p+1);
}
inline void print(i64 x){ if(x<0) print('-'),x=-x; print( (u64)x ); }
}
using namespace HABIT;
//正文开始
i32 d_N,d_M;
ar ar_d_iC,ar_d_C;
inline void add(i32 pos,i64 a){
const i32 ia=a*pos;
for(i32 i=pos;i<=d_N;i+=(i&(-i)))//树状数组基操,不停加 lowbit(i)
ar_d_iC[i]+=ia,ar_d_C[i]+=a;//ici+=ia,ci+=a
}
inline i64 sum(i32 pos){//(pos+1)*求和 ci -求和 ici
i64 d_Ans=0;
for(i32 i=pos;i;i-=(i&(-i))) d_Ans+=ar_d_C[i];
d_Ans*=(pos+1);
for(i32 i=pos;i;i-=(i&(-i))) d_Ans-=ar_d_iC[i];
return d_Ans;
}
inline void update(i32 h,i32 t,i64 a) { add(h,a); add(t+1,-a); }
inline i64 query(i64 h,i64 t) { return sum(t)-sum(h-1); }
int main(){
d_N=read(),d_M=read();
f(i,1,I,d_N) update(i,i,read());
f(i,1,I,d_M)
if(read()==1){
i32 h=read(),t=read();
update(h,t,read());
}
else{
i32 h=read(),t=read();
print(query(h,t)),print('\n');
}
output();
return 0;
}
最后安利以下 本蒟蒻的博客