目录
显示
题目链接
https://www.luogu.com.cn/problem/P4768
题解
本题需要用到一个叫 Kruskal 重构树 的东西。
我们首先分析一下这道题:首先给出一个 \(v\),我们需要找到一条 \(v \to p \to 1\) 的路径,满足:
- \(v \to p\) 的路径全程没有积水(海拔高于水位线);
- \(p \to 1\) 的路径尽可能短。
先解决第二部分。因为图是无向图,所以我们先预处理出 \(1\) 为起点的单源最短路径(记得不要用某死去的算法)。
对于第一部分,我们需要找到任意两点间海拔最低的边最高的一条路径,这个可以通过建立以海拔为边权的最大生成树解决(还记得最大/小生成树一定是瓶颈生成树吗?)。
接下来 Kruskal 重构树就要登场了。
我们在最大生成树上建立重构树,这棵树具有这样的特点:从 \(u\) 到 \(v\) 要经过的最低海拔,正是 \(\text{lca}(u,v)\) 的点权。而且,这棵树还是个小根堆。
也就是说,设水位线为 \(p\),如果 \(u\) 的某个祖先 \(x\) 的点权大于 \(p\),\(u\) 就可以抵达 \(x\) 的所有子节点。
因此我们只需要在所有可以到达的点中,找到距离 \(1\) 号点最近的点即可。
具体来说,我们从根节点开始遍历重构树,遍历到点 \(u\) 的时候,预处理 \(u\) 的子树中到 \(1\) 号点的最短距离。
对于一个起点 \(v\),我们只需倍增向上跳,找到能到达的深度最小的祖先 \(pa\) 即可,则答案就是 \(pa\) 的子树中到 \(1\) 号点的最短距离。
这样我们就完美解决了本题。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <queue> using namespace std; typedef pair<int,int> pii; struct edge { int v,w,next; }e[800005]; struct node { int u,v,l,a; bool operator<(const node&p)const { return a>p.a; } }p[400005]; struct dsu { int fa[400005]; void init(int n) { for(int i=1;i<=n;i++) fa[i]=i; } int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } void set(int x,int y) { fa[x]=y; } }d; int head[200005],dis[200005],vis[200005]; int cnt,n,m; int val[400005],fa[400005][25],mind[400005],dep[400005]; vector<int> tr[400005]; void addedge(int u,int v,int w) { e[++cnt].v=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt; } void dijkstra() { priority_queue<pii,vector<pii>,greater<pii> > q; memset(dis,63,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=e[i].next) { int v=e[i].v; if(dis[v]>dis[u]+e[i].w) { dis[v]=dis[u]+e[i].w; q.push(make_pair(dis[v],v)); } } } } void build_retree() { memset(val,0,sizeof(val)); for(int i=1;i<=2*n;i++) { tr[i].clear(); if(i<=n)mind[i]=dis[i]; else mind[i]=1<<30; } cnt=n; sort(p+1,p+m+1); d.init(2*n); for(int i=1;i<=m;i++) { int u=p[i].u,v=p[i].v; int fu=d.find(u),fv=d.find(v); if(fu!=fv) { cnt++; val[cnt]=p[i].a; d.set(fu,cnt); d.set(fv,cnt); tr[cnt].push_back(fu); tr[cnt].push_back(fv); } } } void dfs(int u,int f) { dep[u]=dep[f]+1; fa[u][0]=f; for(int i=1;i<=20;i++) fa[u][i]=fa[fa[u][i-1]][i-1]; for(auto v:tr[u]) { dfs(v,u); mind[u]=min(mind[u],mind[v]); } } int main() { ios::sync_with_stdio(false); int T; cin>>T; while(T--) { cnt=0; memset(head,0,sizeof(head)); memset(dep,0,sizeof(dep)); memset(fa,0,sizeof(fa)); cin>>n>>m; for(int i=1;i<=m;i++) { cin>>p[i].u>>p[i].v>>p[i].l>>p[i].a; addedge(p[i].u,p[i].v,p[i].l); addedge(p[i].v,p[i].u,p[i].l); } dijkstra(); build_retree(); dfs(cnt,0); int Q,K,S,lastans=0; cin>>Q>>K>>S; while(Q--) { int v,p; cin>>v>>p; v=(v+K*lastans-1)%n+1; p=(p+K*lastans)%(S+1); for(int i=20;i>=0;i--) if(dep[v]>(1<<i)&&val[fa[v][i]]>p)v=fa[v][i]; cout<<(lastans=mind[v])<<endl; } } return 0; }