线段树(标记下传乘法和加法)

Posted 1129-tangqiyuan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树(标记下传乘法和加法)相关的知识,希望对你有一定的参考价值。

传送门:https://www.luogu.org/problem/P3373

标记下传,这种东西就是在求和和更改的时候进行pushdown把标记(各种标记,类似于寒冰标记、痛苦标记、穹妹标记……)下传,来节省时间。

还是挺简单的,主要问题处在pushdown上但多看看就会了

#include<bits/stdc++.h>
using namespace std;
long long n,m,p;
long long multag[1000009];
long long addtag[1000009];
long long seg[4000009];
long long num[1000009];
inline long long kd()
{
    long long x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+(ch^48);ch=getchar();}
    return x*f;
}
inline long long ls(long long x){return x<<1;}
inline long long rs(long long x){return x<<1|1;}
void build(long long now,long long l,long long r)
{
    addtag[now]=0;
    multag[now]=1;
    if(l==r)
    {
        seg[now]=num[l]%p;
        return;
    }
    long long mid=(l+r)>>1;
    build(ls(now),l,mid);
    build(rs(now),mid+1,r);
    seg[now]=(seg[ls(now)]+seg[rs(now)])%p;
    return;
}
void pushdown(long long now,long long l,long long r)
{
    long long mid=(l+r)>>1;
    seg[ls(now)]=(seg[ls(now)]*multag[now]+addtag[now]*(mid-l+1))%p;
    seg[rs(now)]=(seg[rs(now)]*multag[now]+addtag[now]*(r-mid))%p;
    multag[ls(now)]=(multag[ls(now)]*multag[now])%p;
    multag[rs(now)]=(multag[rs(now)]*multag[now])%p;
    addtag[ls(now)]=(addtag[ls(now)]*multag[now]+addtag[now])%p;
    addtag[rs(now)]=(addtag[rs(now)]*multag[now]+addtag[now])%p;
    multag[now]=1;
    addtag[now]=0;
}
void mulchange(long long now,long long l,long long r,long long a,long long b,long long c)
{
    if(r<a||l>b)return;

    if(a<=l&&r<=b)
    {
        seg[now]=(seg[now]*c)%p;
        multag[now]=(multag[now]*c)%p;
        addtag[now]=(addtag[now]*c)%p;
        return;
    }
    pushdown(now,l,r);
    long long mid=(l+r)>>1;
    mulchange(ls(now),l,mid,a,b,c);
    mulchange(rs(now),mid+1,r,a,b,c);
    seg[now]=(seg[ls(now)]+seg[rs(now)])%p;
    return;
}
void addchange(long long now,long long l,long long r,long long a,long long b,long long c)
{    
    if(r<a||l>b)return;
    if(l>=a&&r<=b)
    {
        addtag[now]=(addtag[now]+c)%p;
        seg[now]=(seg[now]+c*(r-l+1))%p;
        return;
    }
    pushdown(now,l,r);
    long long mid=(l+r)>>1;
    addchange(ls(now),l,mid,a,b,c);
    addchange(rs(now),mid+1,r,a,b,c);
    seg[now]=(seg[ls(now)]+seg[rs(now)])%p;
    return;
}
long long query(long long now,long long l,long long r,long long a,long long b)
{
    if(r<a||l>b)return 0;
    if(l>=a&&r<=b)return seg[now];
    pushdown(now,l,r);
    long long mid=(l+r)>>1;
    return (query(ls(now),l,mid,a,b)+query(rs(now),mid+1,r,a,b))%p;
}
int main()
{
    n=kd(),m=kd(),p=kd();
    for(register int i=1;i<=n;i++)
    {
        num[i]=kd();
    }
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        long long pan=kd();
        long long a=kd(),b=kd();
        if(pan==1)
        {
            long long c=kd();
            mulchange(1,1,n,a,b,c);
        }
        if(pan==2)
        {
            long long c=kd();
            addchange(1,1,n,a,b,c);
        }
        if(pan==3)
        {
            cout<<query(1,1,n,a,b)<<endl;
        }
    }
    return 0;
}

 

以上是关于线段树(标记下传乘法和加法)的主要内容,如果未能解决你的问题,请参考以下文章

P2023 [AHOI2009]维护序列 - 线段树区间乘法加法

模拟测试61

线段树常用技巧模版(刷题篇)

线段树常用技巧模版(刷题篇)

[HDOJ4578]Transformation(线段树,多延迟标记)

线段树的复杂操作