【题解】P4768 [NOI2018] 归程

一:【题意】

一张 \(n\) 个点 \(m\) 条边的图。每条边有长度和海拔。
\(q\) 次询问,每次给你起点 \(v\) 和海拔 \(p\),你可以先乘车到一个点然后步行到 \(1\)
乘车只能通过海拔 \(>p\) 的边。
最小化步行到 \(1\) 的距离。

二:【解法】

\(dijkstra\)\(1\) 到每个点 \(x\) 的最短路 \(dis\)
求一个点乘车可到的点,然后对 \(dis\) 取最小值
求一个点只走 \(>p\) 的边可到的点集,是 \(kruskal\) 重构树的经典应用
所以我们建 \(kruskal\) 重构树,对某个子树 \(dis\)\(min\) 即可

三:【代码】

#include<bits/stdc++.h>
#define Pair pair<int,int>
#define w first
#define v second
#define inf 2e9+10
using namespace std;
const int N=4e5+10;int n;
struct Edge{
	int u,v,w,h;
}edg[N];
vector<Pair> mp[N];
int dis[N],vis[N];
void Dijkstra(){
	for(int i=0;i<N;i++) dis[i]=inf;
	for(int i=0;i<N;i++) vis[i]=0;
	priority_queue<Pair,vector<Pair>,greater<Pair> > q;
	q.push({0,1});dis[1]=0;
	while(q.size()){
		int u=q.top().v;q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(auto e:mp[u]){
			int v=e.v,w=e.w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				q.push({dis[v],v});
			}
		}
	}
}
bool cmp(Edge a,Edge b){
	return a.h>b.h;
}
int bcj[N];
int find(int x){
	if(x==bcj[x]) return x;
	return bcj[x]=find(bcj[x]);
}
int nw[N],fa[20][N];
vector<int> sq[N];
int as[N];
void dfs(int u,int pa){
	fa[0][u]=pa;
	as[u]=dis[u];
	for(auto v:sq[u]){
		if(v==pa) continue;
		dfs(v,u);
		as[u]=min(as[u],as[v]);
	}
}
void clears(){
	for(int i=0;i<N;i++){
		mp[i].clear();
		sq[i].clear();
	}
}
void solve(){
	int m;cin>>n>>m;
	clears();
	for(int i=1;i<=m;i++){
		int u,v,w,h;cin>>u>>v>>w>>h;
		edg[i]={u,v,w,h};
		mp[u].push_back({w,v});
		mp[v].push_back({w,u});
	}
	Dijkstra();
	sort(edg+1,edg+1+m,cmp);
	for(int i=1;i<=2*n;i++) bcj[i]=i;
	int cnt=n;
	for(int i=1;i<=n;i++) nw[i]=inf;
	for(int i=1;i<=m;i++){
		int u=edg[i].u,v=edg[i].v,w=edg[i].h;
		u=find(u);v=find(v);
		if(u==v) continue;
		cnt++;
		sq[cnt].push_back(u);
		sq[cnt].push_back(v);
		nw[cnt]=w;
		bcj[u]=bcj[v]=cnt;
	}
	dfs(cnt,0);
	//for(int i=1;i<=cnt;i++) cout<<as[i]<<" ";cout<<"\n";
	for(int k=1;k<20;k++){
		for(int i=1;i<=cnt;i++) fa[k][i]=fa[k-1][fa[k-1][i]];
	}
	
	int q,K,S;cin>>q>>K>>S;
	int ans=0;
	while(q--){
		int x,down;cin>>x>>down;
		x=(x+K*ans-1)%n+1;
		down=(down+K*ans)%(S+1);
		
		for(int i=19;i>=0;i--){
			if(fa[i][x]&&nw[fa[i][x]]>down) x=fa[i][x];
		}
		ans=as[x];
		cout<<ans<<"\n";
	}
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int t;cin>>t;
	while(t--) solve();
	return 0;
}
posted @ 2026-01-10 09:00  Ming3398  阅读(4)  评论(0)    收藏  举报