线段树区间加法+乘法

区间加法+区间乘法(2标记 Luogu

  1. 一开始只想到了开2个标记,但是看了题解才知道标记下传是有优先级的

    • 按题目数据来如果是

      1. 先+后x: $val = (val + b) * k = valk+bk$

      2. 先x后+ : $val = val * k + b$

    • 如何处理2个标记?

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

    $sum=(a[1]+2)*3 + (a[2]+2)*3 + (a[3]+2)*3$;

$sum=(a[1]+2+4)*3+(a[2]+2+4)*3+(a[3]+2+4)*3$;

$=(a[1]+2)3+43+(a[2]+2)3+43+(a[3]+2)3+43;$

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

    $sum=(a[1]+2)*3+4+(a[2]+2)*3+4+(a[3]+2)*3+4$

  • 除非

    $sum=(a[1]+2+4/3)*3+(a[2]+2+4/3)*3+(a[3]+2+4/3)*3$

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

$sum=(a[1]3+23+4)+(a[2]3+23+4)+(a[3]3+23+4)$

1
2
3
4
5
6
// 儿子的倍数都乘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;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#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;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!