线段树2

//区间加区间乘 区间求和
#include<iostream>
#include<cstdio>
#define maxn 100005
#define ls l,m,rt<<1
#define rs l,m,rt<<1|1
long long sum[maxn<<2],lazy_add[maxn<<2],lazy_mu[maxn<<2];
long long a[maxn];//a是原数组 
int n,m,p;
using namespace std;
void push_up(int rt)//更新节点信息,根据题意做不同更新 
{
    sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%p;
}
void build_tree(int l,int r,int rt)
{	lazy_add[rt] = 0, lazy_mu[rt] = 1;
    if(l==r)
    {
        sum[rt]=a[l];
        return;
        }	
    int qu=(l+r)>>1;
    build_tree(l,qu,rt<<1);//更新左子区间 
    build_tree(qu+1,r,rt<<1|1);//更新右子区间
    push_up(rt); 
}
//在区间乘法和区间加法的区别中 主要差异在与push_down在push_down乘法标记时,需要
//将加法标记修改 也就是注意关注新增加的lazy_mu标记即可 
void push_down(int rt,int ln,int rn)//ln,rn为左右子数量 
{	if(!ln || (!rn)) return;
    if(lazy_mu[rt] != 1)//有lazy标记
    {	
        lazy_add[rt<<1]=lazy_add[rt<<1]*lazy_mu[rt]%p;//修改子树的加法标记 
        lazy_add[rt<<1|1]=lazy_add[rt<<1|1]*lazy_mu[rt]%p;//将子树的加法标记乘 
        lazy_mu[rt<<1]=lazy_mu[rt<<1]*lazy_mu[rt]%p;//子树的乘法标记同样乘值 
        lazy_mu[rt<<1|1]=lazy_mu[rt<<1|1]*lazy_mu[rt]%p;
        sum[rt<<1]=sum[rt<<1]*lazy_mu[rt]%p;
        sum[rt<<1|1]=sum[rt<<1|1]*lazy_mu[rt]%p;//子树的值乘上标记 
        //清除已经下推的标记
        lazy_mu[rt]=1; 
    } 
    if(lazy_add[rt])
    {//加法标记可以无视乘法标记加上去 依然具有正确性 
        lazy_add[rt<<1]+=lazy_add[rt];
        lazy_add[rt<<1|1]+=lazy_add[rt];//这里由于加上一个数都是相对标记\
        //所以可能有重复标记,故lazy是在原来的基础上加上现在的标记 
        sum[rt<<1]=(sum[rt<<1]+lazy_add[rt]*ln%p)%p;
        sum[rt<<1|1]=(sum[rt<<1|1]+lazy_add[rt]*rn%p)%p;
        //清除已经下推的标记
        lazy_add[rt]=0; 
    } 
}
void update(int L,int R,long long c,int l,int r,int rt)//LR表示需要操作的大区间
//c表示加上的值 lr表示当前小区间 
{	int qu=(l+r)>>1;
    if(L<=l&&r<=R)//本次小区间全部需要操作
        {
            //传入左右子树的数量
            sum[rt]=(sum[rt]+c*(r-l+1)%p)%p;//可以一次加进去 但需要打上lazy标记 
            lazy_add[rt]=(lazy_add[rt]+c)%p;
            return;
        }		
    push_down(rt,qu-l+1,r-qu);//传入左右子树的数量
    if(L<=qu)update(L,R,c,l,qu,rt<<1);//这里判断是否子树与大区间有交集 
    if(R>qu)update(L,R,c,qu+1,r,rt<<1|1);//如果有则需要递归 进行区间操作 
    push_up(rt);//更新本节点信息
    return; 
}
void mudate(int L,int R,long long c,int l,int r,int rt)//LR表示需要操作的大区间
//c表示加上的值 lr表示当前小区间 
{	//cerr<<"MU: "<<rt<<" "<<L<<" "<<R<<" "<<l<<" "<<r<<" "<<c<<endl;
    int qu=(l+r)>>1;
    if(L<=l&&r<=R)//本次小区间全部需要操作
        {
            
            //cerr<<"MU: "<<rt<<" "<<L<<" "<<R<<" "<<l<<" "<<r<<" "<<c<<" "<<sum[rt]<<endl; 
            sum[rt]=sum[rt]*c%p;//可以一次乘进去 但需要打上lazy标记 
            lazy_add[rt]=lazy_add[rt]*c%p;
            lazy_mu[rt]=lazy_mu[rt]*c%p;
            return;
        }
    push_down(rt,qu-l+1,r-qu);//传入左右子树的数量
    if(L<=qu)mudate(L,R,c,l,qu,rt<<1);//这里判断是否子树与大区间有交集 
    if(R>qu)mudate(L,R,c,qu+1,r,rt<<1|1);//如果有则需要递归 进行区间操作 
    push_up(rt);//更新本节点信息
    return; 
}
long long ask(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)
        return sum[rt]%p;
    int qu=(l+r)>>1;
    push_down(rt,qu-l+1,r-qu);//下推标记寻找答案
    long long ans=0;
    if(L<=qu)ans=(ans+ask(L,R,l,qu,rt<<1)%p)%p;
    if(R>qu)ans=(ans+ask(L,R,qu+1,r,rt<<1|1)%p)%p;
    return ans;
}
void pret()
{
    cin>>n>>m>>p;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    build_tree(1,n,1);
    return;	
}
int main()
{
    pret();
    int ord,x,y;
    long long k;
    for(int i=1;i<=m;i++)
    {
        cin>>ord;
        //cerr<<"drd = "<<ord<<endl;
        if(ord==1)
        {	
            cin>>x>>y>>k;
            mudate(x,y,k,1,n,1);
        }
        if(ord==2)
        {
            cin>>x>>y>>k;
            update(x,y,k,1,n,1);
        }	
        if(ord==3)
        {
            cin>>x>>y;
            cout<<ask(x,y,1,n,1)<<endl;}
        //for(int i=0;i<=n*2;i++)
            //cout<<sum[i]<<" ";
            //cout<<endl;
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值