Content:Segment Tree
Date:2025.7.18

主题

  • 线段树进阶

关于线段树

区间操作

  • 对于区间开根号我们可以记录最大值和最小值,然后维护极差,由此将区间开根号转化为区间加和区间覆盖问题,减小修改操作的复杂(度,均摊后复杂度为 Θ(nlognlog2V)\Theta(n \log n \log^2 V)。(题目:HDU 5828
  • 对于区间取模的操作,我们依然记录最大值,对于 max<Pmax < P 的区间不做修改,从而降低复杂度。(题目:CodeForces 438D
  • 对于区间 gcdgcd 操作,我们可以对原数组进行差分,得到差分数组 dd,而区间 [l,r][l, r]gcdgcd 即为 gcd(al,dl+1,dl+2,,dr)gcd(a_l, d_{l + 1}, d_{l + 2}, \dots, d_{r})。(题目:洛谷 P10463

二维数点问题

  • 二维数点问题是在一个平面中,有若干个点 (xi,yi)(x_i, y_i),询问你在矩形 (a,b)(a, b)(c,d)(c, d) 中包括了多少个点。
  • 离线后的二维数点问题可以用线段树扫描线解决,相当于在线的主席树
  • 题目:CodeForces 1221F
  • Code:
#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5, M = 1e6 + 5;
int n;
vector<int> pos;
vector<pair<int, int>> a[M];

class Point {
public:
int x, y, w;
} points[N];

class SegmentTree {
class Values {
public:
long long max, add;
int index;
};

class Node {
public:
int left, right;
Values val;

Node() = default;
Node(int l, int r) : left(l), right(r), val() {}
};

Node tr[M << 2];

static int get_lc(int k) { return k << 1; }
static int get_rc(int k) { return k << 1 | 1; }
int get_mid(int k) { return (tr[k].left + tr[k].right) >> 1; }

void make_lazy_add(int k, long long val) {
tr[k].val.add += val;
tr[k].val.max += val;
}

void push_up(int k) {
int lc = get_lc(k), rc = get_rc(k);

if (tr[lc].val.max > tr[rc].val.max) {
tr[k].val.max = tr[lc].val.max;
tr[k].val.index = tr[lc].val.index;
} else {
tr[k].val.max = tr[rc].val.max;
tr[k].val.index = tr[rc].val.index;
}
}

void push_down(int k) {
if (tr[k].val.add == 0)
return void();

int lc = get_lc(k), rc = get_rc(k);

make_lazy_add(lc, tr[k].val.add);
make_lazy_add(rc, tr[k].val.add);

tr[k].val.add = 0;
}

public:
void build_tree(int k, int l, int r) {
tr[k] = Node(l, r);

if (tr[k].left == tr[k].right) {
tr[k].val.max = -pos[l - 1];
tr[k].val.index = l;
return void();
}

int mid = get_mid(k);
int lc = get_lc(k), rc = get_rc(k);

build_tree(lc, l, mid);
build_tree(rc, mid + 1, r);

push_up(k);
}

void modify(int k, int l, int r, int val) {
if (tr[k].left >= l && tr[k].right <= r) {
make_lazy_add(k, val);
return void();
}

push_down(k);

int mid = get_mid(k);
int lc = get_lc(k), rc = get_rc(k);

if (r <= mid) {
modify(lc, l, r, val);
} else if (l > mid) {
modify(rc, l, r, val);
} else {
modify(lc, l, mid, val);
modify(rc, mid + 1, r, val);
}

push_up(k);
}

pair<long long, long long> query(int k, int l, int r) {
if (tr[k].left >= l && tr[k].right <= r) {
return make_pair(tr[k].val.max, tr[k].val.index);
}

push_down(k);

int mid = get_mid(k);
int lc = get_lc(k), rc = get_rc(k);

if (r <= mid) {
return query(lc, l, r);
} else if (l > mid) {
return query(rc, l, r);
} else {
return max(query(lc, l, mid), query(rc, mid + 1, r));
}
}
} seg;

int get_index(int val) {
return lower_bound(pos.begin(), pos.end(), val) - pos.begin() + 1;
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);

cin >> n;

for (int i = 1; i <= n; i ++) {
cin >> points[i].x >> points[i].y >> points[i].w;

pos.emplace_back(points[i].x);
pos.emplace_back(points[i].y);

if (points[i].x > points[i].y) {
swap(points[i].x, points[i].y);
}
}

sort(pos.begin(), pos.end());
pos.erase(unique(pos.begin(), pos.end()), pos.end());

int limit = 0;
for (int i = 1; i <= n; i ++) {
points[i].x = get_index(points[i].x);
points[i].y = get_index(points[i].y);

limit = max(limit, points[i].y);

a[points[i].x].emplace_back(points[i].y, points[i].w);
}

seg.build_tree(1, 1, limit);

long long answer = -1ll << 60, left = 0, right = 0;
for (int line = limit; line >= 1; line --) {
for (auto p : a[line]) {
seg.modify(1, p.first, limit, p.second);
}

pair<long long, long long> result = seg.query(1, line, limit);
result.first += pos[line - 1];

if (result.first > answer) {
answer = result.first;
left = pos[line - 1];
right = pos[result.second - 1];
}
}

if (answer < 0) {
answer = 0;
left = 1e9 + 1, right = 1e9 + 1;
}

cout << answer << "\n";
cout << left << " " << left << " " << right << " " << right << '\n';

return 0;
}

线段树分治

  • 线段树分治问题,即在线段树上进行递归,依据线段树的结构性质完成的一类问题。
  • 这类问题的特点是:具有明显的前后/时间关系,且删除操作不易实现,但插入操作可以较容易的实现。
  • 题目:洛谷 P5787
  • Code:
#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;
bool satisfied[N];
int n, m, k, u, v, l, r;

class DSU {
int fa[N << 1], size[N << 1];
stack<pair<int *, int>> stk;

public:
void init(int limit) {
for (int i = 1; i <= limit; i++) fa[i] = i, size[i] = 1;
}

int find(int x) {
if (fa[x] != x) return find(fa[x]);
return x;
}

void merge(int u, int v) {
u = find(u);
v = find(v);
if (u == v) return void();

if (size[u] > size[v]) swap(u, v);

stk.emplace(fa + u, fa[u]);
stk.emplace(size + v, size[v]);
fa[u] = v;
size[v] += size[u];
}

bool check(int x, int y) { return find(x) == find(y); }

int get_version() { return stk.size(); }

void undo(int version) {
while (stk.size() > version) {
*stk.top().first = stk.top().second;
stk.pop();
}
}
} d;

class SegmentTree {
class Node {
public:
int left, right;
vector<pair<int, int>> edges;

Node() = default;
Node(int l, int r) : left(l), right(r) { edges.clear(); }
};

Node tr[N << 2];

static int get_lc(int k) { return k << 1; }
static int get_rc(int k) { return k << 1 | 1; }
int get_mid(int k) { return (tr[k].left + tr[k].right) >> 1; }

public:
void build_tree(int k, int l, int r) {
tr[k] = Node(l, r);

if (tr[k].left == tr[k].right) {
return void();
}

int mid = get_mid(k);
int lc = get_lc(k), rc = get_rc(k);

build_tree(lc, l, mid);
build_tree(rc, mid + 1, r);
}

void add_edge(int k, int l, int r, pair<int, int> edge) {
if (tr[k].left >= l && tr[k].right <= r) {
tr[k].edges.emplace_back(edge);
return void();
}

int mid = get_mid(k);
int lc = get_lc(k), rc = get_rc(k);

if (r <= mid) {
add_edge(lc, l, r, edge);
} else if (l > mid) {
add_edge(rc, l, r, edge);
} else {
add_edge(lc, l, mid, edge);
add_edge(rc, mid + 1, r, edge);
}
}

void dfs(int k) {
bool flag = true;
int version = d.get_version();

for (auto edge : tr[k].edges) {
if (d.check(edge.first, edge.second)) {
flag = false;
break;
} else {
d.merge(edge.first, edge.second + n);
d.merge(edge.first + n, edge.second);
}
}

if (flag == false) {
for (int i = 1; i <= tr[k].right - tr[k].left + 1; i++) {
cout << "No\n";
}
} else {
if (tr[k].left == tr[k].right) {
cout << "Yes\n";
} else {
int lc = get_lc(k), rc = get_rc(k);

dfs(lc);
dfs(rc);
}
}

d.undo(version);
}
} seg;

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);

cin >> n >> m >> k;

d.init(n << 1);
seg.build_tree(1, 1, k);

for (int i = 1; i <= m; i++) {
cin >> u >> v >> l >> r;

if (l == r) continue;
seg.add_edge(1, l + 1, r, make_pair(u, v));
}

seg.dfs(1);

return 0;
}