Luogu P7669 [JOI 2018 Final] 月票购买 / Commuter Pass 题解 [ 紫 ] [ 最短路 ] [ 拓扑排序 ]

月票购买 / Commuter Pass:简单图论题,感觉应该是一眼了,这种板子为啥紫啊。

因为 \(S,T\) 必须选一条最短路径边权变 \(0\),所以可以先跑一遍从 \(S\) 开始的最短路,然后再在反图跑一遍从 \(T\) 开始的最短路,即可求出所有 \(S\to T\) 可能经过的边,也就是满足 \(dis_{S\to i}+w_{i,j}+dis_{j\to T}\) 的边 \((i,j)\)。这些边组成的任意一条 \(S\to T\) 的路径即为 \(S\to T\) 的最短路,且组成的图是个 DAG,因为有环一定不是最短路。

在求出 \(S\to T\) 的拓扑图 \(G\) 后,不难发现我们最多只会走一次这张拓扑图,因为如果走进去之后,走出来再走进去会导致边权为 \(0\) 的边没有用上,一定不优。所以套路地把路径分为三段:\(U\to G,G,G\to V\)。第一和第三种都是容易的,考虑如何做第二种。

进一步观察性质,发现进入拓扑图后我们只可能沿着拓扑图的路径,或沿着拓扑图反图的路径行走。理由很简单,如果逆向走了一条边,此时从 \(S\)\(T\) 只取一条最短路的条件无法被满足,至少要取两条。所以我们对这张图正向和反向跑一个拓扑排序,然后枚举路径的交界处,更新答案即可。

时间复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
using pi=pair<int,int>;
using pl=pair<ll,ll>;
const int N=100005,M=2000005;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll n,m,s,t,ss,tt,odis[N],sdis[N],tdis[N],dp1[N],dp2[N],rd1[N],rd2[N],ans=inf,todis[N];
int h[N],idx=1;
struct Edge{
	int v,ne;
	ll w;
}e[M];
void add(int u,int v,ll w)
{
	e[++idx]={v,h[u],w};
	h[u]=idx;
}
void dijkstra(int spos,ll *dis)
{
	priority_queue<pl,vector<pl>,greater<pl> >q;
	bitset<N>vis;
	dis[spos]=0;
	q.push({0,spos});
	while(!q.empty())
	{
		int u=q.top().se;
		q.pop();
		if(vis[u])continue;
		vis[u]=1;
		for(int i=h[u];i;i=e[i].ne)
		{
			int v=e[i].v;ll w=e[i].w;
			if(dis[v]>dis[u]+w)
			{
				dis[v]=dis[u]+w;
				q.push({dis[v],v});
			}
		}
	}
}
vector<int>g1[N],g2[N];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	memset(odis,0x3f,sizeof(odis));
	memset(todis,0x3f,sizeof(todis));
	memset(sdis,0x3f,sizeof(sdis));
	memset(tdis,0x3f,sizeof(tdis));
	cin>>n>>m;
	cin>>s>>t>>ss>>tt;
	for(int i=1;i<=m;i++)
	{
		ll u,v,w;
		cin>>u>>v>>w;
		add(u,v,w);
		add(v,u,w);
	}
	dijkstra(s,odis);
	dijkstra(t,todis);
	for(int i=2;i<=idx;i++)
	{
		int u=e[i^1].v,v=e[i].v;ll w=e[i].w;
		if(odis[t]==odis[u]+w+todis[v])
		{
			g1[u].push_back(v);
			rd1[v]++;
			g2[v].push_back(u);
			rd2[u]++;
		}
		else if(odis[t]==odis[v]+w+todis[u])
		{
			g1[v].push_back(u);
			rd1[u]++;
			g2[u].push_back(v);
			rd2[v]++;
		}
	}
	dijkstra(ss,sdis);
	dijkstra(tt,tdis);
	memset(dp1,0x3f,sizeof(dp1));
	memset(dp2,0x3f,sizeof(dp2));
	queue<int>q;
	for(int i=1;i<=n;i++)
		if(rd1[i]==0)
			q.push(i);
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		dp1[u]=min(dp1[u],sdis[u]);
		for(auto v:g1[u])
		{
			rd1[v]--;
			if(rd1[v]==0)q.push(v);
			dp1[v]=min(dp1[v],dp1[u]);
		}
	}
	for(int i=1;i<=n;i++)
		if(rd2[i]==0)
			q.push(i);
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		dp2[u]=min(dp2[u],sdis[u]);
		for(auto v:g2[u])
		{
			rd2[v]--;
			if(rd2[v]==0)q.push(v);
			dp2[v]=min(dp2[v],dp2[u]);
		}
	}	
	ans=min(ans,sdis[tt]);
	for(int i=1;i<=n;i++)
		ans=min(ans,min(dp1[i],dp2[i])+tdis[i]);
	cout<<ans;
	return 0;
}
posted @ 2025-07-08 23:13  KS_Fszha  阅读(14)  评论(0)    收藏  举报