区间问题
区间问题
ST表(静态区间查找)
ST表是利用倍增思想来缩短时间的,数组 f[i][j]
的含义,从第 i
个元素开始,向后连续 2^j
个元素组成的区间内的最值(最大值或最小值,需提前确定)。
\[f[i][j] = \max\left(f[i][j-1],\ f\left[i + 2^{j-1}\right][j-1]\right)
\]
void solved()
{
int n, t;
cin >> n >> t;
for (int i = 1; i <= n; i++)
cin >> stmax[i][0];
for (int j = 1; (1 << j) <= n; j++)
{
for (int i = 1; i + (1 << j) - 1 <= n; i++)
{
stmax[i][j] = max(stmax[i][j - 1], stmax[i + (1 << (j - 1))][j - 1]);
}
}
while (t--)
{
int l, r;
cin >> l >> r;
int s = log2(r - l + 1);
cout << max(stmax[l][s], stmax[r + 1 - (1 << s)][s]) << endl;
}
}
树状数组
单修区查
int lowbit(int x)
{
return x & -x;
}
int query(int x)
{
int sum = 0;
for(int i = x; i; i -=lowbit(i)){
sum += c[i];
}
return sum;
}
void update(int x, int v)
{
for(int i = x; i <= n; i += lowbit(i))
{
c[i] += v;
}
}
void solved()
{
cin >> n >> t;
for (int i = 1; i <= n; i++)
{
cin >> arr[i];
update(i, arr[i]);
}
while(t --){
int op, x, y, k;
cin >> op;
if(op == 1){
cin >> x >> k;
update(x, k);
}
else {
cin >> x >> y;
cout << query(y) - query(x - 1) << endl;
}
}
}
区修单查(差分)
把t[i]
构建成差分数组,query(x)
时求(1, x)
前缀和就是点x
的值
//构建差分数组
update(i, x - last);
//修改区间
update(x, k);
update(y + 1, -k);
区修区查
int lowbit(int x)
{
return x & -x;
}
void update(int x, LL c)
{
for (int i = x; i <= n; i += lowbit(i)){
b1[i] += c, b2[i] += x * c;
}
}
LL query(int x)
{
LL res = 0;
for (int i = x; i; i -= lowbit(i)){
res += (x + 1) * b1[i] - b2[i];
}
return res;
}
题目链接二维模板题
二维树状数组模板(区修区查),公式推导过程太复杂,记下就好(其实就是不会)_ 根二维差分数组类似
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 2050;
int n, m ,q;
LL a[N][N], b[N][N], c[N][N], d[N][N];
int lowbit(int x){
return x & -x;
}
void add(int x, int y, LL w){
for(int i = x; i <= n; i += lowbit(i)){
for(int j = y; j <= m; j += lowbit(j)){
a[i][j] += w;
b[i][j] += (x - 1) * w;
c[i][j] += (y - 1) * w;
d[i][j] += (x - 1) * (y - 1) * w;
}
}
}
LL query(int x, int y){
LL res = 0;
for(int i = x; i > 0; i -= lowbit(i)){
for(int j = y; j > 0; j -= lowbit(j)){
res += x * y * a[i][j]
- y * b[i][j]
- x * c[i][j]
+ d[i][j];
}
}
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> q;
while (q --) {
int op, x1, y1, x2, y2;
cin >> op >> x1 >> y1 >> x2 >> y2;
if (op == 1) {
LL v;
cin >> v;
add(x1, y1, v);
add(x1, y2 + 1, -v);
add(x2 + 1, y1, -v);
add(x2 + 1, y2 + 1, v);
} else {
cout <<
query(x2, y2)
- query(x1 - 1, y2)
- query(x2, y1 - 1)
+ query(x1 - 1, y1 - 1)
<< '\n';
}
}
}
线段树
单修区查
struct node
{
int l, r, sum;
} tree[4 * N];
void build(int i, int l, int r)
{
tree[i].l = l;
tree[i].r = r;
if (l == r)
{
tree[i].sum = arr[l];
return;
}
int mid = (l + r) >> 1;
build(i * 2, l, mid);
build(i * 2 + 1, mid + 1, r);
tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum;
}
void update(int i, int x, int k)
{
if (tree[i].l == tree[i].r)
{
tree[i].sum += k;
return;
}
if (x <= tree[i * 2].r)
update(i * 2, x, k);
else
update(i * 2 + 1, x, k);
tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum;
}
int query(int i, int l, int r)
{
if (tree[i].l >= l && tree[i].r <= r)
{
return tree[i].sum;
}
if (tree[i].l > r || tree[i].r < l)
return 0;
int res = 0;
if (tree[i * 2].r >= l)
res += query(i * 2, l, r);
if (tree[i * 2 + 1].l <= r)
res += query(i * 2 + 1, l, r);
return res;
}
区修区查(lazy 延迟更新)
线段树的区间更新(如给 [L, R]
内所有元素加 delta
),如果不使用懒标记,需要递归遍历所有与 [L, R]
重叠的叶子节点并逐个更新,在最坏情况下(如更新整个数组)时间复杂度为 O (n),效率极低。
当更新的区间完全覆盖某个节点的区间时,不立即更新该节点的子节点,而是给该节点打上一个 “标记”,记录需要更新的内容。只有当后续操作(如查询、再次更新)需要访问该节点的子节点时,才将标记 “下推” 到子节点,确保子节点的状态正确。
struct node
{
int l, r, sum, lazy;
} tree[4 * N];
void build(int i, int l, int r)
{
tree[i].l = l;
tree[i].r = r;
tree[i].lazy = 0;
tree[i].sum = 0;
if (l == r)
{
tree[i].sum = arr[l];
return;
}
int mid = (l + r) >> 1;
build(i * 2, l, mid);
build(i * 2 + 1, mid + 1, r);
tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum;
}
void pushdown(int i)
{
if (tree[i].lazy)
{
tree[i * 2].lazy += tree[i].lazy;
tree[i * 2 + 1].lazy += tree[i].lazy;
int mid = (tree[i].l + tree[i].r) >> 1;
tree[i * 2].sum += tree[i].lazy * (mid - tree[i * 2].l + 1);
tree[i * 2 + 1].sum += tree[i].lazy * (tree[i * 2 + 1].r - mid + 1);
tree[i].lazy = 0;
}
}
void update(int i, int l, int r, int k)
{
if (tree[i].l >= l && tree[i].r <= r)
{
tree[i].sum += k * (tree[i].r - tree[i].l + 1);
tree[i].lazy += k;
return;
}
pushdown(i);
if (tree[i * 2].r >= l)
update(i * 2, l, r, k);
if (tree[i * 2 + 1].l <= r)
update(i * 2 + 1, l, r, k);
tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum;
}
int query(int i, int l, int r)
{
if (tree[i].l >= l && tree[i].r <= r)
{
return tree[i].sum;
}
if (tree[i].l > r || tree[i].r < l)
return 0;
pushdown(i);
int res = 0;
if (tree[i * 2].r >= l)
res += query(i * 2, l, r);
if (tree[i * 2 + 1].l <= r)
res += query(i * 2 + 1, l, r);
return res;
}