洛谷 P4768 [NOI2018] 归程
Description
Solution
\(Kruskal\) 重构树好题。
我们先按照水位 \(a\),建 \(Kruskal\) 重构树。具体来讲:按水位从高到低排序,每次选出剩余边中水位最高的一条边插入到树中,这样就建成了一个小根堆。
然后我们再来考虑询问。
对于一个水位线 \(p\),若 \(p < Kruskal\) 重构树上的点 \(x\) 的水位,那么在以 \(x\) 为根的子树中,开车是可以随意通行的,对答案没有贡献。
若 \(p > t[x].dep\) 且 \(p < t[fa[x]].dep\),那么它就不得不在点 \(fa[x]\) 下车,所以对答案的贡献就是从 \(fa[x]\) 到 1 的距离。
那这个距离该如何算呢?
这个很简单,只需要提前跑个 \(dijkstra\) 堆优化预处理一下即可,千万千万千万不要使用 \(spfa\) (逃。
我们对于一组询问 \(v\ p\),找到上述的 \(x\) 节点即可。
现在的问题就是如何找到这样的节点,我们考虑倍增,倍增向上跳(就是个板子),具体见代码。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
inline int read(){
int x = 0;
char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x;
}
const int N = 4e5 + 10;
int T, n, m, last;
struct node{
int u, v, l, a, nxt;
bool operator < (const node &b) const{
return a > b.a;
}
}e[N << 1], tmp[N << 1], edge[N << 1];
int head[N], tot, dis[N];
struct heap{
int x, dis;
bool operator < (const heap &b) const{
return dis > b.dis;
}
};
int f[N], cnt;
inline void Add(int x, int y, int z){
edge[++tot].v = y, edge[tot].l = z, edge[tot].nxt = head[x];
head[x] = tot;
}
inline void dijkstra(){
priority_queue <heap> q;
memset(dis, 0x3f, sizeof(dis));
dis[1] = 0;
q.push((heap){1, 0});
while(!q.empty()){
heap now = q.top();
q.pop();
int x = now.x;
if(dis[x] < now.dis) continue;
for(int i = head[x]; i; i = edge[i].nxt){
int y = edge[i].v;
if(dis[y] > dis[x] + edge[i].l){
dis[y] = dis[x] + edge[i].l;
q.push((heap){y, dis[y]});
}
}
}
}
inline int find(int x){
return f[x] == x ? x : f[x] = find(f[x]);
}
inline void add(int x, int y){
edge[++tot].v = y, edge[tot].nxt = head[x];
head[x] = tot;
}
inline void kruskal(){
sort(e + 1, e + 1 + m);
for(int i = 1; i <= n; i++)
f[i] = i;
cnt = n;
int num = 0;
for(int i = 1; i <= m; i++){
int fu = find(e[i].u), fv = find(e[i].v);
if(fu != fv){
num++;
tmp[++cnt].a = e[i].a;
f[fu] = f[fv] = f[cnt] = cnt;
add(cnt, fu), add(cnt, fv);
}
if(num == n - 1) break;
}
}
int fa[N][20], dep[N];
inline void dfs(int x, int p){
dep[x] = dep[p] + 1, fa[x][0] = p;
for(int i = 1; i <= 19; i++)
fa[x][i] = fa[fa[x][i - 1]][i - 1];
for(int i = head[x]; i; i = edge[i].nxt){
int y = edge[i].v;
dfs(y, x);
tmp[x].l = min(tmp[x].l, tmp[y].l);
}
}
inline int query(int x, int y){
for(int i = 19; i >= 0; i--)
if(dep[x] - (1 << i) > 0 && tmp[fa[x][i]].a > y)
x = fa[x][i];
return tmp[x].l;
}
inline void solve(){
kruskal();
dfs(cnt, 0);
int q = read(), k = read(), s = read();
while(q--){
int x = (k * last + read() - 1) % n + 1, y = (k * last + read()) % (s + 1);
printf("%d\n", last = query(x, y));
}
}
inline void init(){
memset(head, 0, sizeof(head));
memset(fa, 0, sizeof(fa));
memset(f, 0, sizeof(f));
memset(tmp, 0, sizeof(tmp));
memset(edge, 0, sizeof(edge));
last = tot = 0;
}
int main(){
T = read();
while(T--){
init();
n = read(), m = read();
for(int i = 1; i <= m; i++){
e[i].u = read(), e[i].v = read(), e[i].l = read(), e[i].a = read();
Add(e[i].u, e[i].v, e[i].l), Add(e[i].v, e[i].u, e[i].l);
}
dijkstra();
for(int i = 1; i <= n; i++) tmp[i].l = dis[i];
for(int i = n + 1; i <= (n << 1); i++) tmp[i].l = INF;
memset(head, 0, sizeof(head)), tot = 0;
solve();
}
}