[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;
}
posted @ 2020-12-02 18:51  lxzy  阅读(106)  评论(0)    收藏  举报