专题1.5 - 写个线段树板子
专题1.5 - 写个线段树板子
Update
2022.2.13 更新代码,\(ql,qr\)不随着更新与查询改变。
线段树什么功能就不需要多说了,这边主要是讲讲\(lazy\)标记的作用。
普通的线段树,修改的效率为\(\Omicron (n\log n)\),查询效率为\(\Omicron (n\log n)\),修改的效率甚至比直接修改还慢(当然直接修改就没法维护线段树了),那么怎么可以提高修改的效率?
这里需要引入\(lazy\)标记的概念,具体原理如下:
1.修改时,如果修改范围恰好为当前区间范围,停止向下搜索,打上\(lazy\)标记。
2.修改时,如果范围并非恰好,需要向下传递一次\(lazy\)标记。
3.查询时,优先向下传递\(lazy\)标记。
这边给出两个模板:
洛谷P3372
#include <bits/stdc++.h>
#define ll long long
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
using namespace std;
const int maxn = 100010;
ll a[4 * maxn];
ll lazy[4 * maxn];
ll temp[maxn];
void build(int o, int l, int r)
{
if (l == r)
a[o] = temp[l];
else
{
int m = (l + r) / 2;
int ls = 2 * o, rs = 2 * o + 1;
build(ls, l, m);
build(rs, m + 1, r);
a[o] = a[ls] + a[rs];
}
}
void pushdown(int o, int l, int r)
{
int m = (l + r) / 2;
int ls = 2 * o, rs = 2 * o + 1;
lazy[ls] += lazy[o];
lazy[rs] += lazy[o];
a[ls] += lazy[o] * (m - l + 1);
a[rs] += lazy[o] * (r - m);
lazy[o] = 0;
}
void Update(int o, int l, int r, int ql, int qr, int d)
{
if (l >= ql && r <= qr)
{
a[o] += (r - l + 1) * d;
lazy[o] += d;
return;
}
pushdown(o, l, r);
int mid = (l + r) / 2;
int ls = 2 * o, rs = 2 * o + 1;
if (mid >= ql)
Update(ls, l, mid, ql, qr, d);
if (mid < qr)
Update(rs, mid + 1, r, ql, qr, d);
a[o] = a[ls] + a[rs];
}
ll query(int o, int l, int r, int ql, int qr)
{
if (l >= ql && r <= qr)
return a[o];
pushdown(o, l, r);
int mid = (l + r) / 2;
int ls = 2 * o, rs = 2 * o + 1;
ll ans = 0;
if (mid >= ql)
ans += query(ls, l, mid, ql, qr);
if (mid < qr)
ans += query(rs, mid + 1, r, ql, qr);
return ans;
}
int main()
{
fast;
int n, q;
cin >> n >> q;
for (int i = 1; i <= n; i++)
{
cin >> temp[i];
}
memset(lazy, 0, sizeof(lazy));
build(1, 1, n);
while (q--)
{
int opt, l, r, k;
cin >> opt;
if (opt == 1)
{
cin >> l >> r >> k;
Update(1, 1, n, l, r, k);
}
else
{
cin >> l >> r;
cout << query(1, 1, n, l, r) << '\n';
}
}
}
洛谷P3373
#include <bits/stdc++.h>
#define ll long long
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
using namespace std;
const int maxn = 100010;
ll a[4 * maxn];
ll add[4 * maxn];
ll mul[4 * maxn];
ll temp[maxn];
int n, m;
ll p;
void build(int o, int l, int r)
{
if (l == r)
a[o] = temp[l];
else
{
int m = (l + r) / 2;
int ls = 2 * o, rs = 2 * o + 1;
build(ls, l, m);
build(rs, m + 1, r);
a[o] = a[ls] + a[rs];
a[o] = a[o] % p;
}
}
void pushdown(int o, int l, int r)
{
int m = (l + r) / 2;
int ls = 2 * o, rs = 2 * o + 1;
a[ls] = (a[ls] * mul[o] + add[o] * (m - l + 1)) % p;
a[rs] = (a[rs] * mul[o] + add[o] * (r - m)) % p;
mul[ls] = mul[ls] * mul[o] % p;
mul[rs] = mul[rs] * mul[o] % p;
add[ls] = (add[ls] * mul[o] + add[o]) % p;
add[rs] = (add[rs] * mul[o] + add[o]) % p;
mul[o] = 1;
add[o] = 0;
}
void Update_add(int o, int l, int r, int ql, int qr, ll d)
{
if (l >= ql && r <= qr)
{
a[o] = (a[o] + (r - l + 1) * d) % p;
add[o] += d;
return;
}
pushdown(o, l, r);
int mid = (l + r) / 2;
int ls = 2 * o, rs = 2 * o + 1;
if (mid >= ql)
Update_add(ls, l, mid, ql, qr, d);
if (mid < qr)
Update_add(rs, mid + 1, r, ql, qr, d);
a[o] = (a[ls] + a[rs]) % p;
}
void Update_mul(int o, int l, int r, int ql, int qr, ll d)
{
if (l >= ql && r <= qr)
{
a[o] = (a[o] * d) % p;
mul[o] = mul[o] * d % p;
add[o] = add[o] * d % p;
return;
}
pushdown(o, l, r);
int mid = (l + r) / 2;
int ls = 2 * o, rs = 2 * o + 1;
if (mid >= ql)
Update_mul(ls, l, mid, ql, qr, d);
if (mid < qr)
Update_mul(rs, mid + 1, r, ql, qr, d);
a[o] = (a[ls] + a[rs]) % p;
}
ll query(int o, int l, int r, int ql, int qr)
{
if (l >= ql && r <= qr)
return a[o] % p;
pushdown(o, l, r);
int mid = (l + r) / 2;
int ls = 2 * o, rs = 2 * o + 1;
ll ans = 0;
if (mid >= ql)
ans += query(ls, l, mid, ql, qr), ans %= p;
if (mid < qr)
ans += query(rs, mid + 1, r, ql, qr), ans %= p;
return ans % p;
}
int main()
{
fast;
cin >> n >> m >> p;
for (int i = 1; i <= n; i++)
{
cin >> temp[i];
}
for (int i = 1; i <= 4 * n; i++)
{
add[i] = 0;
mul[i] = 1;
}
build(1, 1, n);
while (m--)
{
int opt, x, y, k;
cin >> opt >> x >> y;
if (opt == 2)
{
cin >> k;
Update_add(1, 1, n, x, y, k);
}
if (opt == 1)
{
cin >> k;
Update_mul(1, 1, n, x, y, k);
}
if (opt == 3)
{
cout << query(1, 1, n, x, y) << '\n';
}
}
}
值得注意的是,需要注意向下传递(lazy)的顺序,以及对结果的影响。