维护序列(线段树维护区间乘+区间加)

题目传送门

给定一个长度为n的原序列和模数mod,m个操作,[a,b]区间乘c,[a,b]区间加c,统计[a,b]的区间和。

思路
线段树维护的还是区间和,但是这里我们需要用到两个懒标记,一个记录加法,一个记录乘法,乘法懒标记下传之后要重置为1而不是0。对于一个乘法操作,他影响的是区间和还有这个区间的加法标记、乘法标记。即如果原来这个区间的加法懒标记的值为1,然后传来了一个区间乘5的操作,那么这个加法懒标记也要乘5。

#include
using namespace std;
typedef long long ll;
const int N=1e6+5;
//const int mod=1e9+7;
const int INF=0x7fffffff;
const ll LLINF=0x7fffffffffffffff;
const double EPS=1e-10;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define pb push_back
#define pii pair
#define pll pair
#define debug cout<<"debug"<
#define ls p<<1
#define rs p<<1|1
#define int long long
int a[N];int n,mod;
struct tree
{
    int l,r,pre,add,mul=1;
}t[N<<2];
void build(int p,int x,int y)
{
    t[p].l=x;t[p].r=y;
    if(x==y)
    {
        t[p].pre=a[x]%mod;
        return ;
    }
    int mid=x+y>>1;
    build(ls,x,mid);
    build(rs,mid+1,y);
    t[p].pre=(t[ls].pre%mod+t[rs].pre%mod)%mod;
}
void spread(int p)
{
    int t1=t[p].mul,t2=t[p].add;
    t[ls].pre=(t[ls].pre*t1+t2*(t[ls].r-t[ls].l+1))%mod;
    t[rs].pre=(t[rs].pre*t1+t2*(t[rs].r-t[rs].l+1))%mod;
    t[ls].mul=t[ls].mul*t1%mod;
    t[rs].mul=t[rs].mul*t1%mod;
    t[ls].add=(t[ls].add*t1+t2)%mod;
    t[rs].add=(t[rs].add*t1+t2)%mod;
    t[p].add=0;t[p].mul=1;
    return ;
}
void change1(int p,int x,int y,int z)
{
    if(x<=t[p].l&&t[p].r<=y)
    {
        t[p].pre=t[p].pre*z%mod;
        t[p].mul=z*t[p].mul%mod;
        t[p].add=z*t[p].add%mod;
        return ;
    }
    spread(p);
    int mid=t[p].l+t[p].r>>1;
    if(x<=mid)
        change1(ls,x,y,z);
    if(y>mid)
        change1(rs,x,y,z);
    t[p].pre=(t[ls].pre+t[rs].pre)%mod;
}
void change2(int p,int x,int y,int z)
{
    if(x<=t[p].l&&t[p].r<=y)
    {
        t[p].pre=(t[p].pre+z*(t[p].r-t[p].l+1))%mod;
        t[p].add=(t[p].add+z)%mod;
        return ;
    }
    spread(p);
    int mid=t[p].l+t[p].r>>1;
    if(x<=mid)
        change2(ls,x,y,z);
    if(y>mid)
        change2(rs,x,y,z);
    t[p].pre=(t[ls].pre+t[rs].pre)%mod;
}
int ask(int p,int x,int y)
{
    if(x<=t[p].l&&t[p].r<=y)
    {
        return t[p].pre;
    }
    spread(p);
    int mid=t[p].l+t[p].r>>1;
    int ans=0;
    if(x<=mid)
        ans=(ans+ask(ls,x,y))%mod;
    if(y>mid)
        ans=(ans+ask(rs,x,y))%mod;
    return ans%mod;
}
signed main()
{
    IOS;
    //freopen("","r",stdin);
    //freopen("","w",stdout);

    scanf("%lld%lld",&n,&mod);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",a+i);
    }
    build(1,1,n);
    int m;
    scanf("%lld",&m);
    while(m--)
    {
        int oo,t,g,c;
        scanf("%lld%lld%lld",&oo,&t,&g);
        if(oo==1)
        {
            scanf("%lld",&c);
            change1(1,t,g,c);
        }
        if(oo==2)
        {
            scanf("%lld",&c);
            change2(1,t,g,c);
        }
        if(oo==3)
        {
            int res=ask(1,t,g);
            printf("%lld\n",res);
        }
    }
}

你可能感兴趣的:(线段树)