luogu_P3373 Solution
Problem Description
Now, you have a known series, there are three operations:
-
Multiply \(x\) to all numbers from section \([l, r]\).
-
Add \(x\) to all numbers from section \([l, r]\).
-
Get the sum of section \([l, r]\).
Input Format
The first line contains three integers \(n\), \(m\), \(p\),which represent that the length of the series, the numbers of operations and the mod.
The second line contains \(n\) integers, the \(i^{th}\) integer represents the \(i^{th}\) number of the initial series.
The next \(m\) lines contains multiple integers, each line have a operations. The format is as follows:
- \(1\ x\ y\ k\)
Multiply \(k\) to all numbers from section \([x, y]\).
- \(2\ x\ y\ k\)
Add \(k\) to all numbers from section \([x, y]\).
- \(3\ x\ y\)
Output the sum mod \(p\) of sections \([x, y]\)
Output Format
Output multiple integers, namely the result of all the \(3^{th}\) operations.
Data Range
For the \(30\%\) data: \(n \leq 8\), \(m \leq 10\)
For the \(70\%\) data: \(n \leq 10^{5}\), \(m \leq 10^{4}\)
For the \(100\%\) data: \(n \leq 10^{5}\), \(m \leq 10^{5}\)
Sample Input #1
5 5 38
1 5 4 2 3
2 1 4 1
3 2 5
1 2 4 2
2 3 5 5
3 1 4
Sample Output #1
17
2
Idea of Solving
Obviously, we can use Line Segment Tree to solve this problem. The time complexity is \(O(nlog_{2}^{n})\). But the constant may be so large, so we should give the tree two "lazy" tags. One for the addition, the other for the multiplication.
Now, we have a problem. We don't know the order of the lazy. If there is only addition tag, we can do this problem easily, but how to do if we have two tags?
To solve this problem, we assume we have a node which value is \(x\), now we should first add \(a\) to \(x\), and then multiply \(b\) to \((a + x)\). And the structure of this node as follow.
val: x
left: l
right: r
lazy_add: lazy_add
lazy_multiply: lazy_mul
Obviously, we can do following steps to solve it:
Step 1: Add \(a\) to \(lazy\_add\)
Step 2: Add \(a\) to \(x\)
Step 3: Multiply \(lazy\_mul\) to \(lazy\_add\)
Step 4: Multiply \(b\) to \(lazy\_mul\)
Step 5: Multiply \(b\) to \((a + x)\)
The c++ code:
lazy_add = lazy_add + a;
x = x + a;
lazy_add = lazy_mul * lazy_add;
lazy_mul = lazy_mul * b;
x = x * b;
This means we should do \(lazy\_mul\) multiply \(lazy\_add\) before \(x\) multiply \(b\). And you may find it is correct.
AC Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
ll mod;
struct Tree {
int l, r;
ll add, mul;
ll w;
}tree[maxn];
void pushdown(int root) {
if(!tree[root].add && tree[root].mul == 1) return;
tree[root << 1].mul = tree[root << 1].mul * tree[root].mul % mod;
tree[root << 1].add = (tree[root << 1].add * tree[root].mul % mod + tree[root].add) % mod;
tree[root << 1].w = (tree[root << 1].w * tree[root].mul % mod + tree[root].add * (tree[root << 1].r - tree[root << 1].l + 1) % mod) % mod;
tree[root << 1 | 1].mul = tree[root << 1 | 1].mul * tree[root].mul % mod;
tree[root << 1 | 1].add = (tree[root << 1 | 1].add * tree[root].mul % mod + tree[root].add) % mod;
tree[root << 1 | 1].w = (tree[root << 1 | 1].w * tree[root].mul % mod + tree[root].add * (tree[root << 1 | 1].r - tree[root << 1 | 1].l + 1) % mod) % mod;
tree[root].add = 0;
tree[root].mul = 1;
}
void build(int root, int l, int r) {
tree[root] = Tree{ l, r, 0, 1, 0 };
if(l == r) {
cin >> tree[root].w;
tree[root].w %= mod;
return;
}
int mid = (l + r) >> 1;
build(root << 1, l, mid);
build(root << 1 | 1, mid + 1, r);
tree[root].w = (tree[root << 1].w + tree[root << 1 | 1].w) % mod;
}
void update(int root, int l, int r, int op, ll w) {
if(l <= tree[root].l && tree[root].r <= r) {
if(op == 1) {
tree[root].w = tree[root].w * w % mod;
tree[root].add = tree[root].add * w % mod;
tree[root].mul = tree[root].mul * w % mod;
}
else if(op == 2) {
tree[root].w = tree[root].w + ((tree[root].r - tree[root].l + 1) * w % mod) % mod;
tree[root].add = (tree[root].add + w) % mod;
}
return;
}
pushdown(root);
int mid = (tree[root].l + tree[root].r) >> 1;
if(mid < l) update(root << 1 | 1, l, r, op, w);
else if(mid >= r) update(root << 1, l, r, op, w);
else {
update(root << 1, l, mid, op, w);
update(root << 1 | 1, mid + 1, r, op, w);
}
tree[root].w = (tree[root << 1].w + tree[root << 1 | 1].w) % mod;
}
ll query(int root, int l, int r) {
if(l <= tree[root].l && tree[root].r <= r) return tree[root].w;
pushdown(root);
int mid = (tree[root].l + tree[root].r) >> 1;
if(mid < l) return query(root << 1 | 1, l, r);
else if(mid >= r) return query(root << 1, l, r);
else return (query(root << 1, l, mid) + query(root << 1 | 1, mid + 1, r)) % mod;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m >> mod;
build(1, 1, n);
while(m--) {
int op, l, r;
cin >> op >> l >> r;
if(op == 3) cout << query(1, l, r) << '\n';
else {
ll k;
cin >> k;
k %= mod;
update(1, l, r, op, k);
}
}
return 0;
}