文章目录
有 个节点, 执行 次以下连边操作, 倩输出以 为起点的单源最短路:
若暴力建图, 时空复杂度高达 ,
于是 线段树优化建图 诞生了 .
建立两个 线段树, 分别为 入树 和 出树, 暂且称两者为 , .
按照以下 条规则即可建出一个边数为 的 “优秀” 图 .
- 从 下往上 连边, 则从 上往下 连边 .
- 与 的叶子结点都对应 原节点, 所以两颗树的对应叶子结点之间连上 边权 为 的 无向边.
- 对于上述问题中的 操作, 中 节点直接连向 中的 节点 .
- 对于上述问题中的 操作, 中 节点连向 中 个对应区间节点 .
- 对于上述问题中的 操作, 中的 个区间节点向 中的 连边 .
某些问题只需要实现 , 操作其中之一 ,
- 若实现 操作, 建一个 出树 即可 .
- 若实现 操作, 建一个 入树 即可 .
题外话: 由于初学时不太清楚, 下方例题 无脑建了两颗树, 导致 个点始终超时 …
1.
这里给出 入树 建边, 出树 同理 (以下皆为动态开点) .
void Build(int &k, int l, int r, int opt){
k = ++ node_cnt;
T[k].l = l, T[k].r = r;
if(l == r){
Mp[opt][l] = k; // 原图中 l 对应线段树节点 k.
return ;
}
int mid = l+r >> 1;
Build(T[k].lt, l, mid, opt), Build(T[k].rt, mid+1, r, opt);
if(opt == 0) Add(T[k].lt, k), Add(T[k].rt, k); // In_t 建边.
else Add(k, T[k].lt), Add(k, T[k].rt); // Out_t
}
2.
注意是出树叶子到入树叶子连边 .
void Connect_0(int k){
int l = T[k].l, r = T[k].r;
if(l == r){
Add(Mp[1][l], Mp[0][l]);
return ;
}
Connect_0(T[k].lt), Connect_0(T[k].rt);
}
3.
void Connect(int k, int u, int L, int R){
int l = T[k].l, r = T[k].r;
if(L <= l && r <= R){
if(l == r && u == Mp[1][l]) return ; // 判重边.
Add(u, k); return ;
}
int mid = l+r >> 1;
if(L <= mid) Connect(T[k].lt, u, L, R);
if(R > mid) Connect(T[k].rt, u, L, R);
}
4.
void Connect_2(int k, int L, int R, int v){
int l = T[k].l, r = T[k].r;
if(L <= l && r <= R){
if(l == r && v == Mp[0][l]) return ; //判重边.
Add(k, v); return ;
}
int mid = l+r >> 1;
if(L <= mid) Connect_2(T[k].lt, u, L, R);
if(R > mid) Connect_2(T[k].rt, u, L, R);
}
5.
略.