【笔记】数据结构优化建图(Deepseek 生成)
数据结构优化建图
线段树优化建图
适用场景
处理点与区间、区间与区间之间的连边问题(如:点 \(u\) 向区间 \([L,R]\) 内所有点连边)
实现原理
- 建立两棵线段树:
- 入树(in_tree):处理流入区间的边
- 出树(out_tree):处理流出区间的边
- 节点关系:线段树内部父节点与子节点双向连接(边权为0)
- 时间复杂度:将边数从 \(O(n^2)\) 优化到 \(O(n \log n)\)
C++20 实现
#include <vector>
using namespace std;
struct Edge { int to, w; };
const int N = 1e5 * 20; // 预估节点数
class SegTreeGraph {
vector<Edge> G[N];
int in_id[N], out_id[N];
int node_cnt = 0;
void build(int p, int l, int r, bool is_in) {
if(l == r) {
(is_in ? in_id[l] : out_id[l]) = p;
return;
}
int mid = (l + r) >> 1;
build(p<<1, l, mid, is_in);
build(p<<1|1, mid+1, r, is_in);
// 建立树内边
G[p].emplace_back(p<<1, 0);
G[p].emplace_back(p<<1|1, 0);
if(!is_in) { // 出树需要反向边
G[p<<1].emplace_back(p, 0);
G[p<<1|1].emplace_back(p, 0);
}
}
public:
SegTreeGraph(int n) {
build(1, 1, n, true); // 构建入树
build(1, 1, n, false); // 构建出树
}
void add_edge(int u, int l, int r, int w, bool is_out) {
auto connect = [&](auto&& self, int p, int L, int R) {
if(l <= L && R <= r) {
is_out ? G[u].push_back({p, w})
: G[p].push_back({u, w});
return;
}
int mid = (L + R) >> 1;
if(l <= mid) self(self, p<<1, L, mid);
if(r > mid) self(self, p<<1|1, mid+1, R);
};
connect(connect, 1, 1, n);
}
};
前缀优化建图
适用场景
处理连续前缀/后缀的批量连边(如:点 \(v\) 需要向 \(1 \sim k\) 所有点连边)
C++20 实现
class PrefixGraph {
vector<Edge> G[N];
int pre[N]; // 前缀虚点
public:
void build(int n) {
pre[0] = -1;
for(int i=1; i<=n; ++i) {
pre[i] = ++node_cnt;
G[pre[i]].emplace_back(pre[i-1], 0); // 继承前驱
G[pre[i]].emplace_back(i, 0); // 连接当前点
}
}
void add_prefix(int u, int k, int w) {
G[u].emplace_back(pre[k], w);
}
};
分治优化建图
C++20 实现
class DivideGraph {
vector<Edge> G[N];
int node_cnt = 0;
void build(int l, int r) {
if(l == r) return;
int mid = (l + r) >> 1;
int cur = ++node_cnt;
for(int i=l; i<=mid; ++i)
G[i].emplace_back(cur, 0);
for(int i=mid+1; i<=r; ++i)
G[cur].emplace_back(i, 0);
build(l, mid);
build(mid+1, r);
}
};
倍增优化建图
C++20 实现
class JumpGraph {
vector<Edge> G[N][18]; // G[i][k] 表示i的第2^k层连接
public:
void add_base_edge(int u, int v, int w) {
G[u][0].emplace_back(v, w);
}
void build(int n) {
for(int j=1; j<18; ++j)
for(int i=1; i<=n; ++i)
for(auto [p, w1] : G[i][j-1])
for(auto [q, w2] : G[p][j-1])
G[i][j].emplace_back(q, w1 + w2);
}
};
主席树优化建图
C++20 实现
struct PersistentNode {
int lc, rc;
vector<Edge> edges;
} T[N * 20];
class PersistentGraph {
int roots[N], tot = 0;
public:
int update(int prev, int l, int r, int pos, int v, int w) {
int p = ++tot;
T[p] = T[prev];
if(l == r) {
T[p].edges.emplace_back(v, w);
return p;
}
int mid = (l + r) >> 1;
if(pos <= mid) T[p].lc = update(T[p].lc, l, mid, pos, v, w);
else T[p].rc = update(T[p].rc, mid+1, r, pos, v, w);
return p;
}
};
复杂度对比
| 优化方法 | 边数复杂度 | 空间复杂度 |
|---|---|---|
| 线段树优化 | \(O(n \log n)\) | \(O(n \log n)\) |
| 前缀优化 | \(O(n)\) | \(O(n)\) |
| 分治优化 | \(O(n \log n)\) | \(O(n \log n)\) |
| 倍增优化 | \(O(n \log K)\) | \(O(n \log K)\) |
| 主席树优化 | \(O(q \log n)\) | \(O(q \log n)\) |
注:代码实现采用零开销抽象原则,实际使用时需根据问题规模调整预分配空间

浙公网安备 33010602011771号