洛谷P3373 【模板】线段树2

这题有毒啊,敲了我一晚上加一早上,总算A了。

由于有加和乘两个操作,要用2个lazy数组。

核心难点就是2个lazy数组会相互影响。

因为乘影响加,加不影响乘,所以我们先算乘。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <map>
#include <vector>
#include <list>
#define rep(i,m,n) for(int i=m;i<=n;i++)
#define dop(i,m,n) for(int i=m;i>=n;i--)
#define lowbit(x) (x&(-x)) 
#define ll long long
#define INF 2147483647
using namespace std;
inline int read(){        //不写快读据说可能TLE
   int s=0,w=1;
   char ch=getchar();
   while(ch<='0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
const int maxn=100000;
ll a[maxn+10],sum[(maxn<<2)+10],n,m,b,c,d,e,add[(maxn<<2)+10],mul[(maxn<<2)+10],mod;      //安全起见,都开long long
void PushDown(int now,int m){    //now:线段编号,m:线段长度
    if(mul[now]!=1){                        //乘法lazy数组都赋值为1
      mul[now<<1]*=mul[now];
      mul[now<<1|1]*=mul[now];
      sum[now<<1]*=mul[now];
      sum[now<<1|1]*=mul[now];
      add[now<<1]*=mul[now];
      add[now<<1|1]*=mul[now];
      mul[now<<1]%=mod;
      mul[now<<1|1]%=mod;
      sum[now<<1]%=mod;
      sum[now<<1|1]%=mod;
      add[now<<1]%=mod;
      add[now<<1|1]%=mod;
      mul[now]=1;
    }
    if(add[now]){
      add[now<<1]+=add[now];
      add[now<<1|1]+=add[now];
      sum[now<<1]+=add[now]*(m-(m>>1));
      sum[now<<1|1]+=add[now]*(m>>1);
      add[now<<1]%=mod;        //多mod几下,不会错
      add[now<<1|1]%=mod;
      sum[now<<1]%=mod;
      sum[now<<1|1]%=mod;
      add[now]=0;
    }
}
void Build(int now,int l,int r){   //建树
    mul[now]=1;
    if(l==r) sum[now]=a[l];
    else{
      int mid=(l+r)>>1;
      Build(now<<1,l,mid);
      Build(now<<1|1,mid+1,r);
      sum[now]=sum[now<<1]+sum[now<<1|1];
    }
}
ll Query(int now,int l,int r,int wl,int wr){  //查询
    if(l>wr||r<wl) return 0;
    if(l>=wl&&r<=wr) return sum[now];
    PushDown(now,r-l+1);
    int mid=(l+r)>>1;
    return (Query(now<<1,l,mid,wl,wr)%mod+Query(now<<1|1,mid+1,r,wl,wr)%mod)%mod;
}
void Change(int now,int l,int r,int wl,int wr,int p,int mode){   //改值,mode=1:加法,mode=2:乘法
    if(l>wr||r<wl) return;          //如果当前区间不在需要改值的区间内,返回
    if(l>=wl&&r<=wr){          //如果当前区间包括在需要改值的区间内,改这段的值,并标记lazy
      if(mode) add[now]+=p,sum[now]+=p*(r-l+1),add[now]%=mod,sum[now]%=mod;        
      else mul[now]*=p,sum[now]*=p,add[now]*=p,add[now]%=mod,sum[now]%=mod,mul[now]%=mod;    //如果是乘法,要把加法lazy的值也改掉(乘法分配率)
      return;
    }
    PushDown(now,r-l+1);
    int mid=(l+r)>>1;
    Change(now<<1,l,mid,wl,wr,p,mode);
    Change(now<<1|1,mid+1,r,wl,wr,p,mode);
    sum[now]=(sum[now<<1]+sum[now<<1|1])%mod;
}
int main(){
    n=read();m=read();mod=read();
    rep(i,1,n) a[i]=read();
    Build(1,1,n);
    rep(i,1,m){
       b=read();
       if(b==1){
         c=read();d=read();e=read();
         Change(1,1,n,c,d,e,0);
       }
       if(b==2){
         c=read();d=read();e=read();
         Change(1,1,n,c,d,e,1);
       }
       if(b==3){
         c=read();d=read();
         printf("%lld\n",Query(1,1,n,c,d));
       }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值