Description
有一张n个点m条边的无向图,求删去任意一条边后,从S到T的最短距离的最大值
n, m ≤ 2 × 1 0 5 2 \times 10^5 2×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;
}