[AHOI2009]维护序列
做题时间:2020.12.01
\(【题目描述】\)
有一个长为\(N(N \leq 10^5)\)的序列,支持三种操作,分别是给一段区间\([l,r]\)内的所有数加上、乘上一个数,以及求一段区间\([l,r]\)中的数字的和。
\(【输入样例】\)
7 43
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7
\(【输出样例】\)
2
35
8
\(【考点】\)
线段树
\(【做法】\)
维护两个懒标记\(add_i\)和\(mul_i\),每次执行加法操作时,给对应的\(add_i\)加上那个数;每次进行乘法操作时,给对应的\(mul_i\)和\(add_i\)都乘上那个数。在标记下传时,先下传乘法标记,再下传加法标记。
解释:设当前位的数为\(x\),先加上了\(a_i\),而后又乘上\(b_i\),而后又加上\(c_i\),则最终得数为\((x+a_i) \cdot b_i+c_i=x \cdot b_i +a_i \cdot b_i +c_i\)。在线段树的标记下传中,三次操作后\(add_i=a_i\cdot b_i+c_i\),\(mul_i=b_i\),先下传乘法标记得\(x->x \cdot b_i\),后下传加法标记得\(x \cdot b_i -> x\cdot b_i +a\cdot b_i +c_i\),两者一致。
\(【代码】\)
#include<cstdio>
#include<iomanip>
using namespace std;
typedef long long ll;
const int N=1e5+50;
ll sum[N*4],add[N*4],mul[N*4];
ll a[N];
int n,m,p;
void Mul(int k,int l,int r,ll v)
{
sum[k]*=v,mul[k]*=v,add[k]*=v;//将加法标记和乘法标记都乘上v
sum[k]%=p,mul[k]%=p,add[k]%=p;
}
void Add(int k,int l,int r,ll v)
{
sum[k]+=(r-l+1)*v;
add[k]+=v;//与线段树模板一致
sum[k]%=p,add[k]%=p;
}
void Pushdown(int k,int l,int r)
{
int mid=(l+r)>>1;
if(mul[k]!=1){//先下传乘法标记
Mul(k<<1,l,mid,mul[k]);
Mul(k<<1|1,mid+1,r,mul[k]);
}
if(add[k]){//后下传加法标记
Add(k<<1,l,mid,add[k]);
Add(k<<1|1,mid+1,r,add[k]);
}
add[k]=0,mul[k]=1;
}
void Build(int k,int l,int r)
{
if(l==r){
sum[k]=a[l];
return ;
}
int mid=(l+r)>>1;
Build(k<<1,l,mid);
Build(k<<1|1,mid+1,r);
sum[k]=(sum[k<<1]+sum[k<<1|1])%p;
}
void Modify(int k,int l,int r,int x,int y,ll v,int s)
{
if(l>y||r<x) return ;
if(x<=l&&r<=y){
if(s==2) Add(k,l,r,v);
if(s==1) Mul(k,l,r,v);
return ;
}
Pushdown(k,l,r);
int mid=(l+r)>>1;
Modify(k<<1,l,mid,x,y,v,s);
Modify(k<<1|1,mid+1,r,x,y,v,s);
sum[k]=(sum[k<<1]+sum[k<<1|1])%p;
}
ll Query(int k,int l,int r,int x,int y)
{
if(l>y||r<x) return 0;
if(x<=l&&r<=y) return sum[k];
Pushdown(k,l,r);
int mid=(l+r)>>1;
return (Query(k<<1,l,mid,x,y)+Query(k<<1|1,mid+1,r,x,y))%p;
}
int main()
{
scanf("%d%d",&n,&p);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n*4;i++) mul[i]=1;//乘法标记初始化
Build(1,1,n);
scanf("%d",&m);
int k,t,g;
ll c;
for(int i=1;i<=m;i++){
scanf("%d",&k);
if(k==1){
scanf("%d%d%lld",&t,&g,&c);
Modify(1,1,n,t,g,c,1);
}
if(k==2){
scanf("%d%d%lld",&t,&g,&c);
Modify(1,1,n,t,g,c,2);
}
if(k==3){
scanf("%d%d",&t,&g);
printf("%lld\n",Query(1,1,n,t,g));
}
}
return 0;
}

浙公网安备 33010602011771号