「NOI2018」归程
转化一下题意:我们每次可以从给定的起点出发,沿着海拔高于给定值的边行走,使得最后停下时所处的点离 \(1\) 号点最短路最短。
我们考虑把每个点的权值设成它到 \(1\) 点的最短路,这个可以一遍 \(\text{Dijkstra}\) 求出来。
然后我们发现,由于每次可以走的边都是海拔大于某个值的,我们不难联想到生成树。
我们把所有边按照海拔从高到高排序,构造一颗 \(\text{Kruskal}\) 重构树,那么某一颗子树所包含的原图中的边的海拔都是大于等于该点代表的边的海拔的。
也就是说对于每次的询问,答案的范围都只限制在某一颗子树当中。
那么我们就可以在建树的时候维护一下一颗子树中的最小权值,然后询问的时候倍增跳到海拔满足限制且尽可能小的点,查询子树即可。
参考代码:
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
template < class T > void read(T& s) {
s = 0; int f = 0; char c = getchar();
while ('0' > c || c > '9') f |= c == '-', c = getchar();
while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
s = f ? -s : s;
}
typedef long long LL;
const int _ = 2e5 + 5, __ = 4e5 + 5;
int tot, head[_]; struct Edge { int v, w, nxt; } edge[__ << 1];
void Add_edge(int u, int v, int w) { edge[++tot] = (Edge) { v, w, head[u] }, head[u] = tot; }
int n, m, q, k, s; struct node { int u, v, a; } t[__];
int cmp(node x, node y) { return x.a > y.a; }
int vis[_]; LL dis[_ + __];
void Dijkstra() {
static priority_queue < pair < LL, int > > Q;
memset(dis, 0x3f, sizeof dis);
memset(vis, 0, sizeof vis);
dis[1] = 0, Q.push(make_pair(0, 1));
while (!Q.empty()) {
int u = Q.top().second; Q.pop();
if (vis[u]) continue ; vis[u] = 1;
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].v, w = edge[i].w;
if (dis[v] > dis[u] + w)
dis[v] = dis[u] + w, Q.push(make_pair(-dis[v], v));
}
}
}
int cnt, fa[_ + __], f[19][_ + __], val[_ + __];
int Find(int x) { return fa[x] == x ? x : fa[x] = Find(fa[x]); }
LL query(int v, int p) {
for (int i = 18; ~i; --i)
if (f[i][v] && val[f[i][v]] > p) v = f[i][v];
return dis[v];
}
void solve() {
memset(head, tot = 0, sizeof head);
read(n), read(m), cnt = n;
for (int u, v, w, a, i = 1; i <= m; ++i) {
read(u), read(v), read(w), read(a);
Add_edge(u, v, w), Add_edge(v, u, w), t[i] = (node) { u, v, a };
}
Dijkstra(), sort(t + 1, t + m + 1, cmp);
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1; i <= m; ++i) {
int u = Find(t[i].u), v = Find(t[i].v);
if (u == v) continue ;
f[0][u] = f[0][v] = fa[u] = fa[v] = ++cnt;
fa[cnt] = cnt, dis[cnt] = min(dis[u], dis[v]), val[cnt] = t[i].a;
}
for (int i = 1; i <= 18; ++i)
for (int u = 1; u <= cnt; ++u) f[i][u] = f[i - 1][f[i - 1][u]];
read(q), read(k), read(s);
LL lastans = 0;
for (int v, p, i = 1; i <= q; ++i) {
read(v), read(p);
v = (v + k * lastans - 1) % n + 1;
p = (p + k * lastans) % (s + 1);
printf("%lld\n", lastans = query(v, p));
}
}
int main() {
freopen("return.in", "r", stdin), freopen("return.out", "w", stdout);
int T; read(T);
while (T--) solve();
return 0;
}