线段树区间加法+乘法

区间加法+区间乘法(2标记 Luogu
  1. 一开始只想到了开2个标记,但是看了题解才知道标记下传是有优先级的

    • 按题目数据来如果是

      1. 先+后x: v a l = ( v a l + b ) ∗ k = v a l ∗ k + b ∗ k val = (val + b) * k = val*k+b*k val=(val+b)k=valk+bk

      2. 先x后+ : v a l = v a l ∗ k + b val = val * k + b val=valk+b

    • 如何处理2个标记?

  • 假设给数组[1,2,3]1~3加上2,然后乘3,然后加4
  • 式子:

s u m = ( a [ 1 ] + 2 ) ∗ 3 + ( a [ 2 ] + 2 ) ∗ 3 + ( a [ 3 ] + 2 ) ∗ 3 sum=(a[1]+2)*3 + (a[2]+2)*3 + (a[3]+2)*3 sum=(a[1]+2)3+(a[2]+2)3+(a[3]+2)3;

s u m = ( a [ 1 ] + 2 + 4 ) ∗ 3 + ( a [ 2 ] + 2 + 4 ) ∗ 3 + ( a [ 3 ] + 2 + 4 ) ∗ 3 sum=(a[1]+2+4)*3+(a[2]+2+4)*3+(a[3]+2+4)*3 sum=(a[1]+2+4)3+(a[2]+2+4)3+(a[3]+2+4)3;

= ( a [ 1 ] + 2 ) ∗ 3 + 4 ∗ 3 + ( a [ 2 ] + 2 ) ∗ 3 + 4 ∗ 3 + ( a [ 3 ] + 2 ) ∗ 3 + 4 ∗ 3 ; =(a[1]+2)*3+4*3+(a[2]+2)*3+4*3+(a[3]+2)*3+4*3; =(a[1]+2)3+43+(a[2]+2)3+43+(a[3]+2)3+43;

  • 但是先乘后加 用在 本该先加后乘 的情况下不成立:

s u m = ( a [ 1 ] + 2 ) ∗ 3 + 4 + ( a [ 2 ] + 2 ) ∗ 3 + 4 + ( a [ 3 ] + 2 ) ∗ 3 + 4 sum=(a[1]+2)*3+4+(a[2]+2)*3+4+(a[3]+2)*3+4 sum=(a[1]+2)3+4+(a[2]+2)3+4+(a[3]+2)3+4

  • 除非

s u m = ( a [ 1 ] + 2 + 4 / 3 ) ∗ 3 + ( a [ 2 ] + 2 + 4 / 3 ) ∗ 3 + ( a [ 3 ] + 2 + 4 / 3 ) ∗ 3 sum=(a[1]+2+4/3)*3+(a[2]+2+4/3)*3+(a[3]+2+4/3)*3 sum=(a[1]+2+4/3)3+(a[2]+2+4/3)3+(a[3]+2+4/3)3

  • 但是实数不好处理,所以规定标记下传优先级都为先乘后加 (将先加后乘转化过来)

s u m = ( a [ 1 ] ∗ 3 + 2 ∗ 3 + 4 ) + ( a [ 2 ] ∗ 3 + 2 ∗ 3 + 4 ) + ( a [ 3 ] ∗ 3 + 2 ∗ 3 + 4 ) sum=(a[1]*3+2*3+4)+(a[2]*3+2*3+4)+(a[3]*3+2*3+4) sum=(a[1]3+23+4)+(a[2]3+23+4)+(a[3]3+23+4)

// 儿子的倍数都乘k了,那么儿子的add标记都要变大k倍,且加上父节点的add标记
multi[p<<1]   = (multi[p<<1] * k) % mod;
multi[p<<1|1] = (multi[p<<1|1] * k) % mod;

add[p<<1]   = (add[p<<1]*k + b) % mod;
add[p<<1|1] = (add[p<<1|1]*k + b) % mod;

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 4e5+10;

int a[maxn/4],left_[maxn],right_[maxn];
ll add[maxn],multi[maxn],sum[maxn];
ll mod;

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

void pushdown(int p) {
    ll k = multi[p],b=add[p];
    sum[p<<1]   = (sum[p<<1]*k + b*(right_[p<<1] - left_[p<<1] + 1)) % mod;
    sum[p<<1|1] = (sum[p<<1|1]*k + b*(right_[p<<1|1] - left_[p<<1|1] + 1)) % mod;

    multi[p<<1]   = (multi[p<<1] * k) % mod;
    multi[p<<1|1] = (multi[p<<1|1] * k) % mod;
    add[p<<1]   = (add[p<<1]*k + b) % mod;
    add[p<<1|1] = (add[p<<1|1]*k + b) % mod;

    multi[p] = 1;
    add[p] = 0;
}

void build(int p,int l,int r) {
    multi[p] = 1,add[p]=0;
    left_[p]=l,right_[p]=r;
    if (l == r) {
        sum[p] = a[l];
        return ;
    }
    int mid = (l + r) >> 1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    pushup(p);
}

void change(int p,int l,int r,ll val) {
    if (l<=left_[p] && right_[p]<=r) {
        sum[p] = (sum[p] + val * (right_[p] - left_[p] + 1)) % mod;
        add[p] = (add[p] + val) % mod;
        return ;
    }
    pushdown(p);
    int mid = (left_[p] + right_[p]) >> 1;
    if (l <= mid)
        change(p<<1,l,r,val);
    if (mid < r)
        change(p<<1|1,l,r,val);
    pushup(p);
}

void multiple(int p,int l,int r,ll val) {
    if (l<=left_[p] && right_[p]<=r) {
        sum[p]   = (sum[p] * val) % mod;
        multi[p] = (multi[p] * val) % mod;
        add[p]   = (add[p] * val) % mod;
        return ;
    }
    pushdown(p);
    int mid = (left_[p] + right_[p]) >> 1;
    if (l <= mid)
        multiple(p<<1,l,r,val);
    if (mid < r)
        multiple(p<<1|1,l,r,val);
    pushup(p);
}

ll query(int p,int l,int r) {
    if (l<=left_[p] && right_[p]<=r)
        return sum[p] % mod;
    pushdown(p);
    ll ans = 0;
    int mid = (left_[p] + right_[p]) >> 1;
    if (l <= mid)
        ans = query(p<<1,l,r) % mod;
    if (mid < r)
        ans = (ans + query(p<<1|1,l,r)) % mod;
    return ans % mod;
}

int n,m;

int main() {
    [](){
        ::ios::sync_with_stdio(false);
        ::cin.tie(0);
        ::cout.tie(0);
    }();
    cin >> n >> m >> mod;
    for (int i=1; i<=n; ++i)
        cin >> a[i];
    build(1,1,n);
    while(m--) {
        int index,x,y;
        ll k;
        cin >> index;
        if (index == 1) {
            cin >> x >> y >> k;
            multiple(1,x,y,k);
        } else if (index == 2) {
            cin >> x >> y >> k;
            change(1,x,y,k);
        } else {
            cin >> x >> y;
            cout << query(1,x,y) << "\n";
        }
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值