前言
距离上次学习线段树已经过去了八个月左右,但是实际上忘了不少了,最近遇到不少线段树的题目。感觉有必要复习一下,很多时候写数组形式是最快最方便的,之前的模板都是结构体形式的,更新一下写法。
区间加法+区间查询
//
// Created by Happig on 2020/11/1
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define ENDL "\n"
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 1e5 + 10;
ll tree[maxn << 2], a[maxn], lz[maxn << 2];
void build(int i, int l, int r) {
if (l == r) {
tree[i] = a[l];
return;
}
int mid = (l + r) >> 1, k = i << 1;
build(k, l, mid);
build(k | 1, mid + 1, r);
tree[i] = tree[k] + tree[k | 1];
}
void change(int i, int l, int r, int x, ll y) {
if (l == r) {
tree[i] = y;
return;
}
int mid = (l + r) >> 1, k = i << 1;
if (x <= mid) change(k, l, mid, x, y);
else change(k | 1, mid + 1, r, x, y);
tree[i] = tree[k] + tree[k | 1];
}
void pushdown(int i, int l, int r) {
if (lz[i]) {
int mid = (l + r) >> 1, k = i << 1;
tree[k] += lz[i] * (mid - l + 1), tree[k | 1] += lz[i] * (r - mid);
lz[k] += lz[i], lz[k | 1] += lz[i], lz[i] = 0;
}
}
void add(int i, int l, int r, int x, int y, ll val) {
if (l == x && r == y) {
tree[i] += val * (r - l + 1);
lz[i] += val;
return;
}
pushdown(i, l, r);
int mid = (l + r) >> 1, k = i << 1;
if (y <= mid) add(k, l, mid, x, y, val);
else if (x > mid) add(k | 1, mid + 1, r, x, y, val);
else add(k, l, mid, x, mid, val), add(k | 1, mid + 1, r, mid + 1, y, val);
tree[i] = tree[k] + tree[k | 1];
}
ll query(int i, int l, int r, int x, int y) {
if (l == x && r == y) return tree[i];
pushdown(i, l, r);
int mid = (l + r) >> 1, k = i << 1;
if (y <= mid) return query(k, l, mid, x, y);
else if (x > mid) return query(k | 1, mid + 1, r, x, y);
else return query(k, l, mid, x, mid) + query(k | 1, mid + 1, r, mid + 1, y);
}
int main() {
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
build(1, 1, n);
int op;
int x, y, val;
while (m--) {
cin >> op;
if (op == 1) {
cin >> x >> y >> val;
add(1, 1, n, x, y, val);
} else {
cin >> x >> y;
cout << query(1, 1, n, x, y) << ENDL;
}
}
return 0;
}
区间乘法+区间加法+区间查询
传送门
考虑这样的一张图片,即
p
u
s
h
d
o
w
n
pushdown
pushdown时父节点向子节点下传,设
s
u
m
sum
sum为节点存储的区间和,
m
l
z
mlz
mlz为乘法标记,
a
l
z
alz
alz为加法标记。因为乘法的优先级高于加法,我们规定在标记时先乘后加。那么当我们标记下传时,一般来说都是先更新子节点的
s
u
m
2
sum_2
sum2,因为乘法标记的下传优先,那么
s
u
m
2
=
s
u
m
2
×
m
l
z
1
+
a
l
z
1
×
(
r
2
−
l
2
+
1
)
sum_2 = sum_2 \times mlz_1 + alz_1 \times (r_2-l_2+1)
sum2=sum2×mlz1+alz1×(r2−l2+1)。然后我们考虑更新子节点的两个标记
a
l
z
2
=
m
l
z
1
×
a
l
z
2
+
a
l
z
1
,
m
l
z
2
=
m
l
z
2
×
m
l
z
1
alz_2 = mlz_1 \times alz_2+alz_1,mlz_2 = mlz_2 \times mlz_1
alz2=mlz1×alz2+alz1,mlz2=mlz2×mlz1。
//
// Created by Happig on 2020/11/2
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define ENDL "\n"
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int maxn = 1e5 + 10;
ll tree[maxn << 2], mlz[maxn << 2], alz[maxn << 2], a[maxn << 2];
int p;
void build(int i, int l, int r) {
mlz[i] = 1, alz[i] = 0;
if (l == r) {
tree[i] = a[l] % p;
return;
}
int mid = (l + r) >> 1, k = i << 1;
build(k, l, mid);
build(k | 1, mid + 1, r);
tree[i] = (tree[k] + tree[k | 1]) % p;
}
void pushdown(int i, int l, int r) {
int mid = (l + r) >> 1, k = i << 1;
tree[k] = (mlz[i] * tree[k] % p + alz[i] * (mid - l + 1) % p) % p;
tree[k | 1] = (mlz[i] * tree[k | 1] % p + alz[i] * (r - mid) % p) % p;
alz[k] = (mlz[i] * alz[k] % p + alz[i]) % p;
alz[k | 1] = (mlz[i] * alz[k | 1] % p + alz[i]) % p;
alz[i] = 0;
mlz[k] = mlz[k] * mlz[i] % p;
mlz[k | 1] = mlz[k | 1] * mlz[i] % p;
mlz[i] = 1;
}
void mul(int i, int l, int r, int x, int y, ll val) {
if (l == x && r == y) {
tree[i] = tree[i] * val % p;
mlz[i] = mlz[i] * val % p, alz[i] = alz[i] * val % p;
return;
}
pushdown(i, l, r);
int mid = (l + r) >> 1, k = i << 1;
if (y <= mid) mul(k, l, mid, x, y, val);
else if (x > mid) mul(k | 1, mid + 1, r, x, y, val);
else mul(k, l, mid, x, mid, val), mul(k | 1, mid + 1, r, mid + 1, y, val);
tree[i] = (tree[k] + tree[k | 1]) % p;
}
void add(int i, int l, int r, int x, int y, ll val) {
if (l == x && r == y) {
tree[i] = (tree[i] + val * (r - l + 1) % p) % p;
alz[i] = (alz[i] + val) % p;
return;
}
pushdown(i, l, r);
int mid = (l + r) >> 1, k = i << 1;
if (y <= mid) add(k, l, mid, x, y, val);
else if (x > mid) add(k | 1, mid + 1, r, x, y, val);
else add(k, l, mid, x, mid, val), add(k | 1, mid + 1, r, mid + 1, y, val);
tree[i] = (tree[k] + tree[k | 1]) % p;
}
ll query(int i, int l, int r, int x, int y) {
if (l == x && r == y) {
return tree[i];
}
pushdown(i, l, r);
int mid = (l + r) >> 1, k = i << 1;
if (y <= mid) return query(k, l, mid, x, y);
else if (x > mid) return query(k | 1, mid + 1, r, x, y);
else return (query(k, l, mid, x, mid) + query(k | 1, mid + 1, r, mid + 1, y)) % p;
}
int main() {
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m, op, x, y;
ll val;
cin >> n >> m >> p;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
build(1, 1, n);
while (m--) {
cin >> op;
if (op == 1) {
cin >> x >> y >> val;
mul(1, 1, n, x, y, val);
} else if (op == 2) {
cin >> x >> y >> val;
add(1, 1, n, x, y, val);
} else {
cin >> x >> y;
cout << query(1, 1, n, x, y) << ENDL;
}
}
return 0;
}