Solution
如果不强制在线,那么我们可以将边按照海拔高度从大到小排序,询问也按照水位从大到小排序。
这样,没有被淹的边一定是边排序之后的一个前缀,且可通过边的集合随水位的降低而扩大。
预处理出每个点
u
u
到 的最短路
dis[u]
d
i
s
[
u
]
,在按顺序加边的过程中用并查集维护点之间的连通关系。
这样询问就变成了求在
v
v
的连通块中求 的最小值。
如果强制在线,可以考虑可持久化并查集,但这样是
O(log2)
O
(
log
2
)
的,不能通过较大的数据点。
但继续分析可以发现:我们只需要对历史版本进行查询。
首先,对每个点建一棵树,只包含该点。
加边
(u,v)
(
u
,
v
)
时:
新建节点
w
w
,用并查集找到 和
v
v
格子所在树的根 ,将
w
w
作为 和
Rv
R
v
的父亲。定义节点
w
w
的海拔为边 的海拔。
特别地,如果
u
u
和 已经连通,就不要做任何操作。
容易发现,
w
w
的子树内的点就是这次连通之前 所代表连通块内的点。
所有边都加入后,建树完成。
我们把这棵树叫做 zzq重构树 Kruskal 重构树。
询问时,如何找到海拔
>p
>
p
的边构出的包含
v
v
的连通块呢?
由 Kruskal 重构树的构建过程可以得出—— 不断往祖先跳的过程中,海拔高度单调不增。
所以,我们要找的点
w
w
,就是 的祖先中最后一个(深度最浅)的海拔
>p
>
p
的点。
特别地,如果
v
v
的父亲的海拔就 ,那么
w=v
w
=
v
。
w
w
的子树代表了 所在的连通块。
记录上子树内
dis
d
i
s
的最小值就能得出答案。
如果使用倍增找
w
w
<script type="math/tex" id="MathJax-Element-35">w</script> ,那么复杂度为线性对数级别。
Code
注意此题卡 SPFA
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Edge(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
#define Tree(u) for (int e = adj2[u]; e; e = nxt2[e])
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 2e5 + 5, M = 8e5 + 15, L = 2e6 + 5,
LogN = 21, INF = 0x3f3f3f3f;
int n, m, ecnt, nxt[M], adj[N], go[M], val[M], que[(M << 2) + M],
len, dis[N], QAQ, ecnt2, nxt2[L], adj2[M], go2[L], sph[M], CNT, fa[M],
F[M][LogN], hei[M];
bool vis[N];
struct pyz {
int u, dis;
friend inline bool operator < (pyz a, pyz b) {
return a.dis > b.dis || (a.dis == b.dis && a.u < b.u);
}
friend inline bool operator == (pyz a, pyz b) {
return a.dis == b.dis && a.u == b.u;
}
};
priority_queue<pyz> heap, exheap;
struct cyx {
int u, v, l, a;
} ed[M];
int cx(int x) {
if (fa[x] != x) fa[x] = cx(fa[x]);
return fa[x];
}
inline bool comp(const cyx &a, const cyx &b) {
return a.a > b.a;
}
void add_edge(int u, int v, int w) {
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; val[ecnt] = w;
nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u; val[ecnt] = w;
}
void add_edge2(int u, int v) {
nxt2[++ecnt2] = adj2[u]; adj2[u] = ecnt2; go2[ecnt2] = v;
}
void dfs(int u, int fu) {
int i; F[u][0] = fu;
For (i, 0, 19) F[u][i + 1] = F[F[u][i]][i];
Tree(u) dfs(go2[e], u);
}
void orzdalao() {
while (!heap.empty()) heap.pop();
while (!exheap.empty()) exheap.pop();
memset(dis, INF, sizeof(dis));
memset(vis, 0, sizeof(vis));
int i; dis[1] = 0;
heap.push((pyz) {1, 0});
For (i, 2, n) heap.push((pyz) {i, INF});
For (i, 1, n) {
pyz st = heap.top(); vis[st.u] = 1; heap.pop();
Edge(st.u) if (!vis[v] && dis[st.u] + val[e] < dis[v])
exheap.push((pyz) {v, dis[v]}),
dis[v] = dis[st.u] + val[e],
heap.push((pyz) {v, dis[v]});
while (!heap.empty() && !exheap.empty()
&& heap.top() == exheap.top())
heap.pop(), exheap.pop();
}
}
void work() {
ecnt = ecnt2 = 0; memset(adj, 0, sizeof(adj));
memset(adj2, 0, sizeof(adj2));
int i, u, v, l, a, lst = 0; CNT = n = read(); m = read();
For (i, 1, m) u = read(), v = read(), l = read(), a = read(),
add_edge(u, v, l), ed[i] = (cyx) {u, v, l, a};
sort(ed + 1, ed + m + 1, comp); orzdalao();
For (i, 1, n) sph[fa[i] = i] = dis[i], hei[i] = INF;
For (i, 1, m) {
int frx = cx(ed[i].u), fry = cx(ed[i].v);
if (frx == fry) continue;
fa[++CNT] = CNT;
add_edge2(CNT, frx); add_edge2(CNT, fry);
hei[CNT] = ed[i].a;
sph[CNT] = min(sph[frx], sph[fry]);
fa[frx] = fa[fry] = CNT;
}
dfs(CNT, 0);
int p, q, K, S; q = read(); K = read(); S = read();
while (q--) {
v = read(); p = read();
v = (1ll * K * lst + n + v - 1) % n + 1;
p = (1ll * K * lst + p) % (S + 1);
Rof (i, 20, 0) {
if (!F[v][i]) continue;
if (hei[F[v][i]] > p) v = F[v][i];
}
printf("%d\n", lst = sph[v]);
}
}
int main() {
int T = read(); while (T--) work();
return 0;
}