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;
}

浙公网安备 33010602011771号