Description
有一张n个点m条边的无向图,求删去任意一条边后,从S到T的最短距离的最大值
n, m ≤ 2×1052 \times 10^52×105
Solution
这道题是[USACO09JAN]Safe Travel的变形,然后这是题解
Safe Travel这道题的普遍做法是并查集或树剖,但学长的PDF里提到的是题解讲的可并堆做法,所以我就没采用前两种
然后讲回传送门这题,
首先考虑怎么求删掉一条边后相邻两个点到 T 的最短距离。建出最短路树,如果删掉的不是连向父亲的边,则最短路不变,否则就和Safe Travel一样了
那么怎么求出最终答案呢?可以在图上DP一下
对每个点 u,记 d(u) 表示 u 到 T 的最短路,e(u) 表示删掉它和最短路树上父亲的边后的最短路。令 dp(u) 表示 S = u 时的答案。每次找到 dp 值最小的点来更新其它的点的 dp 值即可。用 u 更新 v 时的转移为 dp(v) =min { max(dp(u) + w(u, v), u == parent v?e(v) : d(v)) }。
Code
//一定不能把S,T反过来,不然就全WA了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<ll,int> pr;
const int inf=0x7fffffff;
const int N=2e5+5;
const int M=2e5+5;
struct Edge{int v,w,nxt,tr;
}edge[M<<1];
int head[N],cnt,vis[N],pre[N];ll dis[N];
priority_queue<pr,vector<pr> ,greater<pr> > q;
int siz[N],dfn[N],ind,parent[N];ll e[N],dp[N];
struct Node{int to,ls,rs,fa,dist;ll val;
}t[M*20];
int tot,rt[N];
int n,m;
void add_edge(int u,int v,int w){edge[++cnt].v=v;edge[cnt].w=w;edge[cnt].nxt=head[u];head[u]=cnt;
}
void dijskra(int s){for(int i=1;i<=n;i++) dis[i]=inf;memset(vis,0,sizeof(vis));dis[s]=0;q.push(make_pair(0,s));while(!q.empty()){pr tmp=q.top();q.pop();int u=tmp.second;if(vis[u]) continue;vis[u]=1;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v,w=edge[i].w;if(dis[v]>dis[u]+1ll*w){dis[v]=dis[u]+1ll*w;pre[v]=i;q.push(pr(dis[v],v));}}}
}
void build(){//建最短路树 for(int i=1;i<=n;i++)if(pre[i]) edge[pre[i]].tr=1;
}
int merge(int a,int b){if(!a||!b) return a+b;if(t[a].val>t[b].val) swap(a,b);t[a].rs=merge(t[a].rs,b);t[t[a].rs].fa=a;if(t[t[a].ls].dist<t[t[a].rs].dist) swap(t[a].ls,t[a].rs);t[a].dist=t[t[a].rs].dist+1;return a;
}
int pop(int a){return merge(t[a].ls,t[a].rs);
}
bool check(int u,int v){return dfn[v]>=dfn[u]&&dfn[v]<=dfn[u]+siz[u]-1;
}
void dfs(int u,int fa){dfn[u]=++ind;siz[u]=1;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(v==fa||!edge[i].tr) continue;parent[v]=u;dfs(v,u);rt[u]=merge(rt[u],rt[v]);siz[u]+=siz[v];}for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(v==fa||edge[i].tr) continue;//(u,fa)也是树边,不考虑 t[++tot]=(Node){v,0,0,tot,0,dis[u]+dis[v]+edge[i].w};rt[u]=merge(rt[u],tot); }while(check(u,t[rt[u]].to)) rt[u]=pop(rt[u]);e[u]=rt[u]?t[rt[u]].val-dis[u]:inf;
}
void get_ans(int s){for(int i=1;i<=n;i++) dp[i]=inf;memset(vis,0,sizeof(vis));dp[s]=0;q.push(make_pair(0,s));while(!q.empty()){pr tmp=q.top();q.pop();int u=tmp.second;if(vis[u]) continue;vis[u]=1;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v,w=edge[i].w;int t=max(dp[u]+w,parent[v]==u?e[v]:dis[v]);if(dp[v]>t){dp[v]=t;q.push(pr(dp[v],v));}}}
}
int main(){scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int u,v,w;scanf("%d%d%d",&u,&v,&w);add_edge(u,v,w);add_edge(v,u,w);}dijskra(n);build();tot=n;dfs(n,0);get_ans(n);if(dp[1]==inf) printf("%d\n",-1); else printf("%lld\n",dp[1]);return 0;
}