UESTC - 1597 线段树~~~区间更新 区间求和

本文介绍了一种使用双懒惰传播优化线段树的数据结构实现方法。该方法通过维护两个懒惰标记,分别用于加法和乘法操作,实现了区间修改和查询功能。文章详细解释了如何处理加法和乘法懒惰标记的顺序问题,确保了操作的正确性和效率。
/*
思路:这个题目主要是用两个延迟标记的数组,一个加一个乘来处理。
但是我们的问题是,当两个延迟标记到了 一个区间的时候我们是先加还是乘呢。
如果弄清楚这个原理,这个题目,就出来了。
大家看下这个啥 忘记叫啥了(sum[rt]+3)*2 ==sum*2+3*2,我们假设+3就是线段树加的延迟标记。
乘2就是线段树的乘的延迟标记。
我们遇到乘,就直接也同样的更新加的延迟标记,因为加如果没有的话,乘就是1,加就是0,当加来的时候
我们每次都默认先乘,当有加的时候乘的就是1所以不变,当只有成的时候加的就是0, sum*a+3*b 当a等于1
b等于0的时候,我们执行这个结果是不会改变的。当有乘延迟来的时候 就得将之前的 加的延迟标记  也得乘上这个数,
就相当于哪里 +3 也得乘二,就是( sum[rt]+3)*2 ==sum*2+3*2 这样子 
*/ 
#include<stdio.h>
#include<string.h>
#include<stdlib.h> 
#define mem(a,b) memsesum(a,b,sizeof(a))
using namespace std;
#define N 100000*4+10
typedef long long ll;
ll ssss;
ll n,p,m;
ll sum[N];//求和数组	 
ll add[N];//延迟标记加 
ll add1[N];//乘 
void PushUp(int rt)//更新 
{
     sum[rt]=(sum[rt*2]+sum[rt*2+1])%p;
}
void build(ll l,ll r,ll rsum)//建立线段树 
{
    add1[rsum]=1,add[rsum]=0;
    if(l==r)
    {
        scanf("%lld",&sum[rsum]);
        return;
    }
    ll m=(l+r)>>1;
    build(l,m,rsum<<1);
    build(m+1,r,rsum<<1|1);
    PushUp(rsum);
}
void pushdown(ll rsum,ll num)
{
    add[rsum<<1]=(add[rsum<<1]*add1[rsum]+add[rsum])%p;
    add[rsum<<1|1]=(add[rsum<<1|1]*add1[rsum]+add[rsum])%p;
    add1[rsum<<1]=(add1[rsum<<1]*add1[rsum])%p;
    add1[rsum<<1|1]=(add1[rsum<<1|1]*add1[rsum])%p;
    sum[rsum<<1]=(sum[rsum<<1]*add1[rsum]+(num-num/2)*add[rsum])%p;
    sum[rsum<<1|1]=(sum[rsum<<1|1]*add1[rsum]+(num/2)*add[rsum])%p;
    add[rsum]=0;
    add1[rsum]=1;
}
void updasuma(ll rsum,ll l,ll r,ll x,ll y,ll val,ll op)
{
    if(x<=l&&r<=y)
    {
        if(op==1)
        {
            add[rsum]=(add[rsum]*val)%p;
            add1[rsum]=(add1[rsum]*val)%p;
            sum[rsum]=(sum[rsum]*val)%p;
        }
        else
        {
            add[rsum]=(add[rsum]+val);
            sum[rsum]=(sum[rsum]+(r-l+1)*val)%p;
        }
        return;
    }
    pushdown(rsum,r-l+1);
    ll m=(l+r)>>1;
    if(x<=m)
        updasuma(rsum<<1,l,m,x,y,val,op);
    if(y>m)
        updasuma(rsum<<1|1,m+1,r,x,y,val,op);
    PushUp(rsum);
}
void query(ll x,ll y,ll l,ll r,ll rsum)
{
    if(x<=l&&y>=r)
      {
         ssss=(ssss%p+sum[rsum]%p)%p;
         return ;
      }
    pushdown(rsum,r-l+1);
    ll m=(l+r)>>1;

    if(x<=m)
        query(x,y,l,m,rsum<<1);
     if(y>m)
      query(x,y,m+1,r,rsum<<1|1);

}
int main()
{
    scanf("%lld%lld",&n,&p);
    build(1,n,1);
    scanf("%lld",&m);
    ll x,y,val,op;
    for(ll i=0;i<m;i++)
    {
        scanf("%lld",&op);
        if(op==1)
        {
            scanf("%lld%lld%lld",&x,&y,&val);
           updasuma(1,1,n,x,y,val,1);
        }
        else if(op==2)
        {
            scanf("%lld%lld%lld",&x,&y,&val);
          updasuma(1,1,n,x,y,val,2);
        }
        else
        {
            scanf("%lld%lld",&x,&y);
                ssss=0;
            query(x,y,1,n,1);

            printf("%lld\n",ssss);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值