题意
- 对于一个长为n的序列a,你需要完成q次询问,每次询问有四种情况
- 输出(l,r)区间和
- 输出(l,r)区间平方和
- (l,r)所有元素+=x
- (l,r)所有元素*=x
- 其中n为1e4量级,m为2e5量级
思路
- 线段树变种
- 对于区间和,维护第一个线段树
- 对于区间平方和,维护第二个线段树
- 对于所有元素加上定值,使用第1个lazy标记
- 对于所有元素乘上定值,使用第2个lazy标记
- 对于加法运算和乘法运算混合出出现,永远将lazy记录的式子转化为kx+b,即每次乘上一个因子s时,转化为ksx+sb
- 对于lazy标记的pushdown
- 乘法lazy初值是1,加法初值是0
- 先更新lazy
lazy_add[son]=lazy_add[son]*lazy_mul[fa]+lazy_add[fa]
lazy_mul[son]=lazy_mul[son]*lazy_mul[fa]
- 然后要先转移平方的线段树,因为平方线段树依赖于正常的线段树
- 由
变为
展开右式 
- 其中第一部分和第三部分分别对应了两个线段树的值,中间部分是

- 最后转移正常线段树
- 对于查询,每次查询也是需要pushdown的不然会找错值
- 对于修改,修改完全包含区间的时候转移方式和pushdown类似
代码
#include<bits/stdc++.h>
#define int ll
using namespace std;
using ll=long long;
ll a[10101];
ll tre1[4*10101];
ll tre2[4*10101];
ll laz_mul[4*10101];
ll laz_add[4*10101];
void build1(int p,int l,int r){
if(l==r){
tre1[p]=a[l];
return ;
}
int mid=(r+l)>>1;
build1(p*2,l,mid);
build1(p*2+1,mid+1,r);
tre1[p]=tre1[p*2]+tre1[p*2+1];
}
void build2(int p,int l,int r){
if(l==r){
tre2[p]=a[l]*a[l];
return ;
}
int mid=(r+l)>>1;
build2(p*2,l,mid);
build2(p*2+1,mid+1,r);
tre2[p]=tre2[p*2]+tre2[p*2+1];
}
void pushdown(int p,int l,int r){
laz_mul[p*2]*=laz_mul[p];
laz_mul[p*2+1]*=laz_mul[p];
laz_add[p*2]=laz_add[p*2]*laz_mul[p]+laz_add[p];
laz_add[p*2+1]=laz_add[p*2+1]*laz_mul[p]+laz_add[p];
int mid=(r+l)>>1;
tre2[p*2]=tre2[p*2]*laz_mul[p]*laz_mul[p]+2*laz_mul[p]*laz_add[p]*tre1[p*2]+laz_add[p]*laz_add[p]*(mid-l+1);
tre2[p*2+1]=tre2[p*2+1]*laz_mul[p]*laz_mul[p]+2*laz_mul[p]*laz_add[p]*tre1[p*2+1]+laz_add[p]*laz_add[p]*(r-mid);
tre1[p*2]=tre1[p*2]*laz_mul[p]+laz_add[p]*(mid-l+1);
tre1[p*2+1]=tre1[p*2+1]*laz_mul[p]+laz_add[p]*(r-mid);
laz_add[p]=0;
laz_mul[p]=1;
}
ll cal1(int p,int l,int r,int x,int y){
if(x<=l&&r<=y){
return tre1[p];
}
if(laz_add[p]!=0||laz_mul[p]!=1) pushdown(p,l,r);
int mid=(l+r)>>1;
if(x>mid) return cal1(p*2+1,mid+1,r,x,y);
if(y<=mid) return cal1(p*2,l,mid,x,y);
return cal1(p*2+1,mid+1,r,x,y)+cal1(p*2,l,mid,x,y);
}
ll cal2(int p,int l,int r,int x,int y){
if(x<=l&&r<=y){
return tre2[p];
}
if(laz_add[p]!=0||laz_mul[p]!=1) pushdown(p,l,r);
int mid=(l+r)>>1;
if(x>mid) return cal2(p*2+1,mid+1,r,x,y);
if(y<=mid) return cal2(p*2,l,mid,x,y);
return cal2(p*2+1,mid+1,r,x,y)+cal2(p*2,l,mid,x,y);
}
void mul(int p,int l,int r,int x,int y,int num){
if(x<=l&&r<=y){
tre1[p]*=num;
tre2[p]*=num*num;
laz_add[p]*=num;
laz_mul[p]*=num;
return ;
}
if(laz_add[p]!=0||laz_mul[p]!=1) pushdown(p,l,r);
int mid=(r+l)>>1;
if(x<=mid) mul(p*2,l,mid,x,y,num);
if(y>mid) mul(p*2+1,mid+1,r,x,y,num);
tre1[p]=tre1[p*2]+tre1[p*2+1];
tre2[p]=tre2[p*2]+tre2[p*2+1];
}
void add(int p,int l,int r,int x,int y,int num){
if(x<=l&&r<=y){
tre2[p]+=num*num*(r-l+1)+2*num*tre1[p];
tre1[p]+=(r-l+1)*num;
laz_add[p]+=num;
return ;
}
if(laz_add[p]!=0||laz_mul[p]!=1) pushdown(p,l,r);
int mid=(r+l)>>1;
if(x<=mid) add(p*2,l,mid,x,y,num);
if(y>mid) add(p*2+1,mid+1,r,x,y,num);
tre1[p]=tre1[p*2]+tre1[p*2+1];
tre2[p]=tre2[p*2]+tre2[p*2+1];
}
signed main(){
int n,m;
cin >> n >> m ;
for(int i=1;i<=n;i++){
cin >> a[i];
}
for(int i=0;i<=4*n;i++){
laz_add[i]=0;
laz_mul[i]=1;
}
build1(1,1,n);
build2(1,1,n);
for(int i=0;i<m;i++){
int op;
cin >> op;
if(op==1){
int l,r;
cin >> l >> r;
cout << cal1(1,1,n,l,r) << endl;
}else if(op==2){
int l,r;
cin >> l >> r;
cout << cal2(1,1,n,l,r) << endl;
}else if(op==3){
int l,r,x;
cin >> l >> r >> x;
mul(1,1,n,l,r,x);
}else{
int l,r,x;
cin >> l >> r >> x;
add(1,1,n,l,r,x);
}
}
return 0;
}