[Awing]维护序列 线段树,区间操作

前言

传送门 :
好久没写,线段树了,没想到被难到了

思路

区间操作 对应 懒标记

两个区间操作 即 两个懒标记

Q: 两个懒标记应该先计算哪个 ?

如果先计算加法再计算乘法,那么加法一定需要被乘数整除才行

因此先使用加法

对于每个数我们都看成 a ∗ b + c a*b+c ab+c
因此对于 乘法懒标记 我们可以
b ∗ m u l b*mul bmul

对于加法懒标记
c ∗ m u l + a d d c*mul + add cmul+add

然后就是不好写 QAQ

CODE

#include <bits/stdc++.h>
using namespace std;
#define ll  long long
#define ned '\n'
const int N = 1e5+10;

int n,p,m;
int w[N];
struct node
{
    int l,r;
    int sum,add,mul; ///两个懒标记
}tr[N*4];///四倍空间

void pushup(int u)
{
    tr[u].sum  = (tr[u<<1].sum + tr[u<<1|1].sum)%p;
}

inline void eval(node &t,int add,int mul) ///因为经常 +一个*一个
{
    t.sum =((ll)t.sum * mul +(ll)(t.r - t.l +1)*add)%p;
    t.mul = (ll)t.mul*mul%p;
    t.add = ((ll) t.add * mul + add )%p;
}

void pushdown(int u)
{
    eval(tr[u<<1],tr[u].add,tr[u].mul);
    eval(tr[u<<1|1],tr[u].add,tr[u].mul);
    tr[u].add = 0 ,tr[u].mul = 1;
}
void build(int u,int l,int r)
{
    if(l == r)
    tr[u] = {l,r,w[r],0,1};
    else
    {
        tr[u] = {l,r,0,0,1};
        int mid  = l+r>>1;
        build(u<<1,l,mid),build(u<<1|1,mid+1,r);
        pushup(u);
    }
}

void modify(int u,int l,int r,int add,int mul)
{
    if(tr[u].l>=l &&tr[u].r<=r)
    eval(tr[u],add,mul); ///如果在区间内,更新
    else
    {
        pushdown(u);
        int mid  = tr[u].l +tr[u].r>>1;
        if(l<=mid)
        modify(u<<1,l,r,add,mul);
        if(r>mid)
        modify(u<<1|1,l,r,add,mul);
        pushup(u);
    }
}

int query(int u,int l,int r)
{
    if(tr[u].l >= l  &&tr[u].r<=r)
    return tr[u].sum;

    pushdown(u);
    int mid  = tr[u].l +tr[u].r>>1;
    int sum  = 0 ;
    if(l<=mid)
    sum = query(u<<1,l,r);
    if(r>mid)
    sum = (sum+query(u<<1|1,l,r))%p;
    return sum;
}
inline void solve()
{
    cin>>n>>p;
    for(int i=1;i<=n;i++)
    cin>>w[i];
    build(1,1,n);

    cin>>m;
    while(m -- )
    {
        int op,l,r,d;
        cin>>op>>l>>r;

        if(op  == 1)
        {
            cin>>d;
            modify(1,l,r,0,d);
        }
        else if(op == 2)
        {
            cin>>d;
            modify(1,l,r,d,1);
        }
        else
        cout<<query(1,l,r)<<ned;
    }
}
int main()
{
    ios::sync_with_stdio(false);
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值