题目:https://www.luogu.org/problemnew/show/P2023
分析:
大方向是线段数,难点在于如何处理懒惰标记下传。
思路一:
结点k存有mul[k],add[k],sum[k],即乘的系数、加的系数、和的值。
现在对于这个结点对应区间所有元素需要加上b2,(相当于先乘1,再加b2),或者乘上a2(相当于先乘a2,再加0),
根据数学知识,由a2(a1x+b1)+b2=a1a2x+a2b1+b2得到
sum[k]=a2*sum[k]+b2*(r-l+1),mul[k]=a2*mul[k],add[k]=a2*add[k]+b2,
接着令这个结点的mul[k]=1,add[k]=0,即lazy_tag清零。
关键代码如下:
void Add_Mul(int k,int l,int r,long long a,long long b){
mul[k]=mul[k]*a%p;
add[k]=(add[k]*a%p+b%p)%p;
sum[k]=(a*sum[k]%p+b*(r-l+1)%p)%p;
}
void pushdown(int k,int l,int r){
if(add[k]==0 && mul[k]==1 || (l==r) ) return;
//如果没有标记,或是叶子,则直接返回
int mid=(l+r)/2;
Add_Mul(2*k,l,mid,mul[k],add[k]);
Add_Mul(2*k+1,mid+1,r,mul[k],add[k]);
mul[k]=1;//标记清零
add[k]=0;
}
思路二:
先处理乘的系数, 清零乘的标记,再处理加的系数,清零加的标记。
关键代码如下:
void Add(int k,int l,int r,int v){
add[k]=(add[k]+v)%p;
sum[k]=(sum[k]+(long long)v*(r-l+1))%p;
}
void Mul(int k,int l,int r,int v){
add[k]=(add[k]*v)%p;
mul[k]=mul[k]*v%p;
sum[k]=sum[k]*v%p;
}
void pushdown(int k,int l,int r,int mid){
if(add[k]==0 && mul[k]==1) return ;
Mul(k*2,l,mid,add2[k]);
Mul(k*2+1,mid+1,r,add2[k]);
mul[k]=1;//清零乘的标记
Add(k*2,l,mid,add1[k]);
Add(k*2+1,mid+1,r,add1[k]);
add[k]=0;//清零和的标记
}
TLE的思路:
如果当前结点有标记,否则传给儿子,
如果儿子还有标记,则再往下传,一直下传到结点没有标记为止,然后返回时清零。
会T掉5个或6个点。
注意事项:要开long long,否则会RE。
AC代码(思路一)如下:
#include<cstdio>
#include<iostream>
using namespace std;
const int Maxn=1e5+5;
int n,p,m,a[Maxn];
long long add[4*Maxn],mul[4*Maxn],sum[4*Maxn];
void build_tree(int k,int l,int r){
if(l==r){
mul[k]=1;
add[k]=0;
sum[k]=a[l]%p;
return;
}
int mid=(l+r)/2;
build_tree(2*k,l,mid);
build_tree(2*k+1,mid+1,r);
mul[k]=1;add[k]=0;
sum[k]=(sum[2*k]+sum[2*k+1])%p;
}
void Add_Mul(int k,int l,int r,long long a,long long b){
mul[k]=mul[k]*a%p;
add[k]=(add[k]*a%p+b%p)%p;
sum[k]=(a*sum[k]%p+b*(r-l+1)%p)%p;
}
void pushdown(int k,int l,int r){
if(add[k]==0 && mul[k]==1 || (l==r) ) return;
//如果没有标记,或是叶子,则直接返回
int mid=(l+r)/2;
Add_Mul(2*k,l,mid,mul[k],add[k]);
Add_Mul(2*k+1,mid+1,r,mul[k],add[k]);
mul[k]=1;add[k]=0;//标记清零
}
void modify(int k,int l,int r,int x,int y,long long a,long long b){
if(l>y || r<x) return;
pushdown(k,l,r);
if(l>=x && r<=y) {
Add_Mul(k,l,r,a,b);
return;//切勿漏写!!
}
int mid=(l+r)/2;
modify(2*k,l,mid,x,y,a,b);
modify(2*k+1,mid+1,r,x,y,a,b);
sum[k]=(sum[2*k]+sum[2*k+1])%p;
}
long long query(int k,int l,int r,int x,int y){
if(l>y || r<x) return 0;
if(l>=x && r<=y) return sum[k];
pushdown(k,l,r);
int mid=(l+r)/2;
return ( query(2*k,l,mid,x,y)+query(2*k+1,mid+1,r,x,y) )%p;
}
int main(){
cin>>n>>p;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
build_tree(1,1,n);
print();
cin>>m;
int id,x,y;
long long c;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&id,&x,&y);
if(id==1){
scanf("%lld",&c);
modify(1,1,n,x,y,c,0);
}
else if(id==2){
scanf("%lld",&c);
modify(1,1,n,x,y,1,c);
}
else printf("%lld\n",query(1,1,n,x,y));
}
return 0;
}