线段树是一棵二叉树,每个节点存储了区间的左端点,右端点,和对应区间上的某个数据(如果使用延迟更新,还有相应的懒惰标记)
维护区间和的线段树(更新操作只有区间加)
(洛谷P3372)
#include <bits/stdc++.h>
#define MAXN 100005
#define LEFT(i) ((i) << 1)
#define RIGHT(i) ((i) << 1 | 1)
using namespace std;
struct Node {
int l;
int r;
long long data;
long long lz;
};
Node tree[4 * MAXN];
int input[MAXN];
void pushdown(int i) {
if (tree[i].lz) {
tree[LEFT(i)].lz += tree[i].lz;
tree[RIGHT(i)].lz += tree[i].lz;
tree[LEFT(i)].data +=
tree[i].lz * (tree[LEFT(i)].r - tree[LEFT(i)].l + 1);
tree[RIGHT(i)].data +=
tree[i].lz * (tree[RIGHT(i)].r - tree[RIGHT(i)].l + 1);
tree[i].lz = 0;
}
}
void build(int i, int l, int r) {
tree[i].l = l, tree[i].r = r;
if (l == r) {
tree[i].data = input[l];
return;
}
int mid = (l + r) >> 1;
build(LEFT(i), l, mid);
build(RIGHT(i), mid + 1, r);
tree[i].data = tree[LEFT(i)].data + tree[RIGHT(i)].data;
}
long long search(int i, int l, int r) {
if (l <= tree[i].l && r >= tree[i].r)
return tree[i].data;
if (tree[i].l > r || tree[i].r < l)
return 0;
pushdown(i);
long long s = 0;
if (tree[LEFT(i)].r >= l)
s += search(LEFT(i), l, r);
if (tree[RIGHT(i)].l <= r)
s += search(RIGHT(i), l, r);
return s;
}
void add(int i, int l, int r, long long k) {
if (l <= tree[i].l && r >= tree[i].r) {
tree[i].data += k * (tree[i].r - tree[i].l + 1);
tree[i].lz += k;
return;
}
if (tree[i].l > r || tree[i].r < l)
return;
pushdown(i);
if (tree[LEFT(i)].r >= l)
add(LEFT(i), l, r, k);
if (tree[RIGHT(i)].l <= r)
add(RIGHT(i), l, r, k);
tree[i].data = tree[LEFT(i)].data + tree[RIGHT(i)].data;
}
/*void dfs(int i) {
if (tree[i].l == tree[i].r) {
printf("%d ", tree[i].data);
return;
}
pushdown(i);
dfs(LEFT(i));
dfs(RIGHT(i));
}*/
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++)
scanf("%d", &input[i]);
build(1, 0, n - 1);
while (m--) {
int opt, l, r;
scanf("%d%d%d", &opt, &l, &r);
l--, r--;
if (opt == 1) {
long long k;
scanf("%ld", &k);
add(1, l, r, k);
} else if (opt == 2) {
printf("%ld\n", search(1, l, r));
}
}
return 0;
}
维护区间和的线段树(区间更新操作有区间加和区间乘)
(洛谷P3373)
#include <bits/stdc++.h>
#define MAXN 100005
#define LEFT(i) ((i) << 1)
#define RIGHT(i) ((i) << 1 | 1)
using namespace std;
struct Node {
int l;
int r;
long long data;
long long plz;
long long mlz;
};
Node tree[4 * MAXN];
int input[MAXN];
int p;
void pushdown(int i) {
long long k1 = tree[i].mlz, k2 = tree[i].plz;
tree[LEFT(i)].data = (k1 * tree[LEFT(i)].data +
k2 * (tree[LEFT(i)].r - tree[LEFT(i)].l + 1)) %
p;
tree[RIGHT(i)].data = (k1 * tree[RIGHT(i)].data +
k2 * (tree[RIGHT(i)].r - tree[RIGHT(i)].l + 1)) %
p;
tree[LEFT(i)].mlz = (tree[LEFT(i)].mlz * k1) % p;
tree[RIGHT(i)].mlz = (tree[RIGHT(i)].mlz * k1) % p;
tree[LEFT(i)].plz = (k1 * tree[LEFT(i)].plz + k2) % p;
tree[RIGHT(i)].plz = (k1 * tree[RIGHT(i)].plz + k2) % p;
tree[i].plz = 0;
tree[i].mlz = 1;
}
void build(int i, int l, int r) {
tree[i].l = l, tree[i].r = r;
tree[i].mlz = 1, tree[i].plz = 0;
if (l == r) {
tree[i].data = input[l];
return;
}
int mid = (l + r) >> 1;
build(LEFT(i), l, mid);
build(RIGHT(i), mid + 1, r);
tree[i].data = (tree[LEFT(i)].data + tree[RIGHT(i)].data) % p;
}
long long search(int i, int l, int r) {
if (l <= tree[i].l && r >= tree[i].r)
return tree[i].data;
if (tree[i].l > r || tree[i].r < l)
return 0;
pushdown(i);
long long s = 0;
if (tree[LEFT(i)].r >= l)
s += search(LEFT(i), l, r) % p;
if (tree[RIGHT(i)].l <= r)
s += search(RIGHT(i), l, r) % p;
return s % p;
}
void add(int i, int l, int r, long long k) {
if (l <= tree[i].l && r >= tree[i].r) {
tree[i].data = (tree[i].data + k * (tree[i].r - tree[i].l + 1)) % p;
tree[i].plz = (tree[i].plz + k) % p;
return;
}
if (tree[i].l > r || tree[i].r < l)
return;
pushdown(i);
if (tree[LEFT(i)].r >= l)
add(LEFT(i), l, r, k);
if (tree[RIGHT(i)].l <= r)
add(RIGHT(i), l, r, k);
tree[i].data = (tree[LEFT(i)].data + tree[RIGHT(i)].data) % p;
}
void mul(int i, int l, int r, long long k) {
if (l <= tree[i].l && r >= tree[i].r) {
tree[i].data = (tree[i].data * k) % p;
tree[i].mlz = (tree[i].mlz * k) % p;
tree[i].plz = (tree[i].plz * k) % p;
return;
}
if (tree[i].l > r || tree[i].r < l)
return;
pushdown(i);
if (tree[LEFT(i)].r >= l)
mul(LEFT(i), l, r, k);
if (tree[RIGHT(i)].l <= r)
mul(RIGHT(i), l, r, k);
tree[i].data = (tree[LEFT(i)].data + tree[RIGHT(i)].data) % p;
}
int main() {
int n, m;
scanf("%d%d%d", &n, &m, &p);
for (int i = 0; i < n; i++)
scanf("%d", &input[i]);
build(1, 0, n - 1);
while (m--) {
int opt, l, r;
scanf("%d%d%d", &opt, &l, &r);
l--, r--;
if (opt == 1) {
long long k;
scanf("%ld", &k);
mul(1, l, r, k);
} else if (opt == 2) {
long long k;
scanf("%ld", &k);
add(1, l, r, k);
} else if (opt == 3) {
printf("%ld\n", search(1, l, r));
}
}
return 0;
}
懒惰标记的理解
对于有多种更新操作的线段树,关键是lazytag(懒惰标记)的处理,无非是操作的先后顺序不同。
假设操作1把
x
x
x经过操作数
k
k
k映射到了
f
(
x
,
k
)
f(x,k)
f(x,k),操作2把
x
x
x经过操作数
k
k
k映射到了
g
(
x
,
k
)
g(x,k)
g(x,k)。此时两种对应的懒标记名称为
f
l
z
flz
flz和
g
l
z
glz
glz。如果秉承先处理操作
f
f
f后处理操作
g
g
g的原则,原来节点的值
x
x
x就变成了
g
(
f
(
x
,
f
l
z
)
,
g
l
z
)
g(f(x,flz),glz)
g(f(x,flz),glz)。现在它的父节点传来一组标记
f
l
z
′
flz'
flz′和
g
l
z
′
glz'
glz′。
那么要对当前节点继续更新,节点的值
g
(
f
(
x
,
f
l
z
)
,
g
l
z
)
g(f(x,flz),glz)
g(f(x,flz),glz)就应该变成
g
(
f
(
g
(
f
(
x
,
f
l
z
)
,
g
l
z
)
,
f
l
z
′
)
,
g
l
z
′
)
g(f(g(f(x,flz),glz),flz'),glz')
g(f(g(f(x,flz),glz),flz′),glz′)。
此时如何进行懒标记的更新呢?
那就要想办法将
g
(
f
(
g
(
f
(
x
,
f
l
z
)
,
g
l
z
)
,
f
l
z
′
)
,
g
l
z
′
)
g(f(g(f(x,flz),glz),flz'),glz')
g(f(g(f(x,flz),glz),flz′),glz′)化成
g
(
f
(
x
,
F
)
,
G
)
g(f(x,F),G)
g(f(x,F),G)的形式。此时,只需要将
f
l
z
flz
flz更新为
F
F
F,将
g
l
z
glz
glz更新成
G
G
G即可。
以具体例子为例:
f
(
x
,
k
)
=
k
x
f(x,k)=kx
f(x,k)=kx
g
(
x
,
k
)
=
x
+
k
g(x,k)=x+k
g(x,k)=x+k
g
(
f
(
x
,
f
l
z
)
,
g
l
z
)
=
f
l
z
⋅
x
+
g
l
z
g(f(x,flz),glz)=flz\cdot x+glz
g(f(x,flz),glz)=flz⋅x+glz
此时
g
(
f
(
g
(
f
(
x
,
f
l
z
)
,
g
l
z
)
,
f
l
z
′
)
,
g
l
z
′
)
g(f(g(f(x,flz),glz),flz'),glz')
g(f(g(f(x,flz),glz),flz′),glz′)
=
f
l
z
′
(
f
l
z
⋅
x
+
g
l
z
)
+
g
l
z
′
=flz'(flz\cdot x+glz)+glz'
=flz′(flz⋅x+glz)+glz′
=
f
l
z
′
⋅
f
l
z
⋅
x
+
f
l
z
′
⋅
g
l
z
+
g
l
z
′
=flz'\cdot flz\cdot x+flz'\cdot glz+glz'
=flz′⋅flz⋅x+flz′⋅glz+glz′
=
g
(
f
(
x
,
f
l
z
′
⋅
f
l
z
)
,
f
l
z
′
⋅
g
l
z
+
g
l
z
′
)
=g(f(x,flz'\cdot flz),flz'\cdot glz+glz')
=g(f(x,flz′⋅flz),flz′⋅glz+glz′)
所以,
F
=
f
l
z
′
⋅
f
l
z
F=flz'\cdot flz
F=flz′⋅flz
G
=
f
l
z
′
⋅
g
l
z
+
g
l
z
′
G=flz'\cdot glz+glz'
G=flz′⋅glz+glz′
在第二个例子中,对应的pushdown更新模式也是这样的
void pushdown(int i) {
long long k1 = tree[i].mlz, k2 = tree[i].plz;
tree[LEFT(i)].data = (k1 * tree[LEFT(i)].data +
k2 * (tree[LEFT(i)].r - tree[LEFT(i)].l + 1)) %
p;
tree[RIGHT(i)].data = (k1 * tree[RIGHT(i)].data +
k2 * (tree[RIGHT(i)].r - tree[RIGHT(i)].l + 1)) %
p;
tree[LEFT(i)].mlz = (tree[LEFT(i)].mlz * k1) % p;
tree[RIGHT(i)].mlz = (tree[RIGHT(i)].mlz * k1) % p;
tree[LEFT(i)].plz = (k1 * tree[LEFT(i)].plz + k2) % p;
tree[RIGHT(i)].plz = (k1 * tree[RIGHT(i)].plz + k2) % p;
tree[i].plz = 0;
tree[i].mlz = 1;
}
在加法和乘法的过程中,也需要对自身的lazytag进行更新,而且原则要和pushdown的原则一致。
加
k
k
k:
相当于某个神秘力量传来一组懒惰标记
(
f
l
z
′
,
g
l
z
′
)
=
(
1
,
k
)
(flz',glz')=(1,k)
(flz′,glz′)=(1,k)
代入
F
=
f
l
z
′
⋅
f
l
z
F=flz'\cdot flz
F=flz′⋅flz
G
=
f
l
z
′
⋅
g
l
z
+
g
l
z
′
G=flz'\cdot glz+glz'
G=flz′⋅glz+glz′
懒标记更新方式为:
F
=
f
l
z
F=flz
F=flz
G
=
g
l
z
+
k
G=glz+k
G=glz+k
对应代码:
void add(int i, int l, int r, long long k) {
if (l <= tree[i].l && r >= tree[i].r) {
tree[i].data = (tree[i].data + k * (tree[i].r - tree[i].l + 1)) % p;
tree[i].plz = (tree[i].plz + k) % p;
return;
}
if (tree[i].l > r || tree[i].r < l)
return;
pushdown(i);
if (tree[LEFT(i)].r >= l)
add(LEFT(i), l, r, k);
if (tree[RIGHT(i)].l <= r)
add(RIGHT(i), l, r, k);
tree[i].data = (tree[LEFT(i)].data + tree[RIGHT(i)].data) % p;
}
乘
k
k
k:
相当于某个神秘力量传来一组懒惰标记
(
f
l
z
′
,
g
l
z
′
)
=
(
k
,
0
)
(flz',glz')=(k,0)
(flz′,glz′)=(k,0)
代入
F
=
f
l
z
′
⋅
f
l
z
F=flz'\cdot flz
F=flz′⋅flz
G
=
f
l
z
′
⋅
g
l
z
+
g
l
z
′
G=flz'\cdot glz+glz'
G=flz′⋅glz+glz′
懒标记更新方式为:
F
=
k
⋅
f
l
z
F=k\cdot flz
F=k⋅flz
G
=
k
⋅
g
l
z
G=k\cdot glz
G=k⋅glz
对应代码:
void mul(int i, int l, int r, long long k) {
if (l <= tree[i].l && r >= tree[i].r) {
tree[i].data = (tree[i].data * k) % p;
tree[i].mlz = (tree[i].mlz * k) % p;
tree[i].plz = (tree[i].plz * k) % p;
return;
}
if (tree[i].l > r || tree[i].r < l)
return;
pushdown(i);
if (tree[LEFT(i)].r >= l)
mul(LEFT(i), l, r, k);
if (tree[RIGHT(i)].l <= r)
mul(RIGHT(i), l, r, k);
tree[i].data = (tree[LEFT(i)].data + tree[RIGHT(i)].data) % p;
}