题解 P1186 【玛丽卡】
题目大意
给定一张
n 个点m 条边的带权无向图。现在需要删除一条边,使得1 到n 的最短路的长度最大,输出这个最大长度。
题解
大多数错误的做法是枚举最短路上的边并删除,然后重新跑一遍最短路。如果用优先队列优化(劣化)的
但可以发现,虽然我们不大能快速求出删除了一条边后的
下文中,设
可以证明,
下面考虑怎么求出这个连续段的开头和结尾。一种可行的方法是,分别以
整理一下我们要做的事情:
- 求出从
1 到n 的最短路\mathcal{E} 。 - 分别求出
1 和n 到达每个点的最短路上最后的\mathcal{E} 上的点的标号。 - 枚举每条边并硬点它必然被选。求出新的最短路的长度,更新在最短路路径上
A_u,B_v 两点间所有的边的权值。 - 枚举最短路上的所有边,取所有权值的最大值。
关于第二步,考虑使用并查集。因为跑完单源最短路后本质上形成了一棵类似于树一样的东西,每个节点都有一个前驱节点。我们把
关于第
虽然证明内容写起来蛮多的,也不大容易理解,但是代码还算好写……总复杂度
参考代码
#include<bits/stdc++.h>
#define up(l,r,i) for(int i=l,END##i=r;i<=END##i;++i)
#define dn(r,l,i) for(int i=r,END##i=l;i>=END##i;--i)
using namespace std;
typedef long long i64;
const int INF =1e9;
const int MAXN=1e3+3;
namespace Seg{
const int SIZ=MAXN*4; int T[SIZ];
#define lc(t) (t<<1)
#define rc(t) (t<<1|1)
void bld(int t,int a,int b){
T[t]=INF; if(a==b) return; int c=a+b>>1;
bld(lc(t),a,c),bld(rc(t),c+1,b);
}
void sst(int t,int a,int b,int l,int r,int w){
if(l<=a&&b<=r) T[t]=min(T[t],w); else {
int c=a+b>>1;
if(l<=c) sst(lc(t),a,c ,l,r,w);
if(r> c) sst(rc(t),c+1,b,l,r,w);
}
}
int qry(int t,int a,int b,int p){
if(a==b) return T[t]; int c=a+b>>1;
if(p<=c) return min(T[t],qry(lc(t),a,c ,p));
else return min(T[t],qry(rc(t),c+1,b,p));
}
}
namespace Slv{
int n,m,o,ans; bool U[MAXN][MAXN];
int A[MAXN],B[MAXN],W[MAXN][MAXN]; bool V[MAXN];
int P[MAXN],Q[MAXN],I[MAXN];
int qread(){
int w=1,c,ret;
while((c=getchar())> '9'||c< '0') w=(c=='-'?-1:1); ret=c-'0';
while((c=getchar())>='0'&&c<='9') ret=ret*10+c-'0';
return ret*w;
}
void dij(int s,int t,int *D,int *E){
up(1,n,i) D[i]=INF,V[i]=false; D[s]=0; up(1,n,i){
int f=0;
up(1,n,j) if((!f||D[j]<D[f])&&!V[j]) f=j; V[f]=true;
up(1,n,j) if(!V[j]&&D[f]+W[f][j]<D[j]) D[j]=D[f]+W[f][j],E[j]=f;
}
if(!o){
for(int p=t;p;p=E[p]) I[p] =--o; o=-o;
for(int p=t,q=t;p;q=p,p=E[p]) I[p]+=o+1,U[p][q]=true;
}
up(1,n,i) if(I[i]) E[i]=i;
}
int gtf(int x,int *F){return x==F[x]?x:F[x]=gtf(F[x],F);}
void slv(){
n=qread(),m=qread();
up(1,n,i) up(1,n,j) W[i][j]=INF;
up(1,m,i){
int u=qread(),v=qread(),w=qread();
if(w<W[u][v]) W[u][v]=W[v][u]=w;
}
dij(1,n,A,P),dij(n,1,B,Q),Seg::bld(1,1,o);
up(1,n,i) up(1,n,j) if(!U[i][j]&&!U[j][i]){
int w=A[i]+W[i][j]+B[j],s=I[gtf(i,P)],t=I[gtf(j,Q)];
if(s<t) Seg::sst(1,1,o,s,t-1,w);
}
up(1,o-1,i) ans=max(ans,Seg::qry(1,1,o,i));
printf("%d\n",ans);
}
}
int main(){
Slv::slv(); return 0;
}