P4768 [NOI2018] 归程
给定一个 n n n个点, m m m条边的无向联通图,边的描述为 [ u , v , l , a ] [u, v, l, a] [u,v,l,a],表示 u u u, v v v连有一条长度为 l l l,海拔为 a a a的边,
有 Q Q Q个询问,每次给出一个出发点 u u u和一个海拔限制高度 p p p,并且在出发点有一辆车,这辆车可以通过海拔大于 p p p的边,
问,从 u − > 1 u->1 u−>1的最短步行长度是什么多少。
设从 u u u坐车出发可到的点集为 S S S,我们的任务就是找到一个点 v , v ∈ S v, v \in S v,v∈S, d i s ( v , 1 ) dis(v, 1) dis(v,1)是 d i s ( x , 1 ) , x ∈ S dis(x, 1),x \in S dis(x,1),x∈S中最的小。
① 预处理出每个点到点 1 1 1的最短路径出来,
② 我们按照海拔高度降序建立一颗 k r u s k a l kruskal kruskal重构树,
③ 从 u u u号点往上跳,找到可坐车到达的深度最小的节点 r t rt rt,显然从 u u u可坐车到达的点集就是 r t rt rt所在的这颗子树,
④ 由于我们查找的是最小值,所以只需在 d f s dfs dfs的过程中,不断向上更新整颗子树的最小值即可。
⑤ 直接输出我们找到的 r t rt rt所代表的答案。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int head[N], to[N], nex[N], cnt = 1;
int head1[N], to1[N], nex1[N], value1[N], cnt1 = 1;
int vis[N], dis[N], ff[N], value[N], fa[N][21], ans[N], nn, n, m, Q, K, S;
struct Edge {
int u, v, w;
bool operator < (const Edge &t) const {
return w > t.w;
}
}edge[N];
struct Node {
int u, w;
bool operator < (const Node &t) const {
return w > t.w;
}
};
void add1(int x, int y, int w) {
to1[cnt1] = y;
nex1[cnt1] = head1[x];
value1[cnt1] = w;
head1[x] = cnt1++;
}
void add(int x, int y) {
to[cnt] = y;
nex[cnt] = head[x];
head[x] = cnt++;
}
priority_queue<Node> q;
void Dijkstra() {
while (q.size()) {
q.pop();
}
q.push({1, 0});
memset(vis, 0, sizeof vis), memset(dis, 0x3f, sizeof dis);
dis[1] = 0;
while (q.size()) {
int rt = q.top().u;
q.pop();
if (vis[rt]) {
continue;
}
vis[rt] = 1;
for (int i = head1[rt]; i; i = nex1[i]) {
if (dis[to1[i]] > dis[rt] + value1[i]) {
dis[to1[i]] = dis[rt] + value1[i];
q.push({to1[i], dis[to1[i]]});
}
}
}
}
int find(int rt) {
return ff[rt] == rt ? rt : ff[rt] = find(ff[rt]);
}
void dfs(int rt, int f) {
fa[rt][0] = f, ans[rt] = rt <= n ? dis[rt] : 0x3f3f3f3f;
for (int i = 1; i <= 20; i++) {
fa[rt][i] = fa[fa[rt][i - 1]][i - 1];
}
for (int i = head[rt]; i; i = nex[i]) {
if (to[i] == f) {
continue;
}
dfs(to[i], rt);
ans[rt] = min(ans[rt], ans[to[i]]);
}
}
void kruskal() {
for (int i = 1; i < N; i++) {
ff[i] = i, head[i] = 0;
}
cnt = 1;
sort(edge + 1, edge + 1 + m);
for (int i = 1, cur = 1; i <= m && cur < n; i++) {
int u = find(edge[i].u), v = find(edge[i].v);
if (u != v) {
cur++, nn++;
ff[u] = nn, ff[v] = nn;
value[nn] = edge[i].w;
add(nn, u), add(nn, v);
if (u <= n) {
value[u] = edge[i].w;
}
if (v <= n) {
value[v] = edge[i].w;
}
}
}
dfs(nn, 0);
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
scanf("%d", &T);
while (T--) {
scanf("%d %d", &n, &m);
nn = n;
memset(head1, 0, sizeof head1), cnt1 = 1;
for (int i = 1, u, v, l, a; i <= m; i++) {
scanf("%d %d %d %d", &u, &v, &l, &a);
add1(u, v, l), add1(v, u, l);
edge[i] = {u, v, a};
}
Dijkstra();
kruskal();
scanf("%d %d %d", &Q, &K, &S);
for (int i = 1, v, p, last_ans = 0; i <= Q; i++) {
scanf("%d %d", &v, &p);
v = (v + 1ll * K * last_ans - 1) % n + 1, p = (p + 1ll * K * last_ans) % (S + 1);
for (int j = 20; j >= 0; j--) {
if (fa[v][j] && value[fa[v][j]] > p) {
v = fa[v][j];
}
}
last_ans = ans[v];
printf("%d\n", last_ans);
}
}
return 0;
}