题解:P14184 有向无权图删边最短路
masterhuang · · 题解
upd on 2025/10/17:修改了错误内容,现在时间复杂度是正确的,可以不管评论区提到的错误了。
如有不对请再次提出,我会虚心改正的。
本文摘抄自 EI's blog,稍微进行了一些补充。
我们先求出一条
于是我们只需要考察这条路径上每条边被删除的时候的最短路。
对比原来的最短路,容易发现删边后 只需要离开原来的最短路一次。
具体的:记原来的最短路为
考虑设置阈值
短
注意到从
我们可以一次性处理所有
从它们开始做多源 BFS,可以在
具体怎么做呢?
称路径上
多源 BFS 时候起点要有初始权值
然后考虑按顺序加入关键点,假设加入到
最后再做一个类似后缀
这部分复杂度为
长
随机选一个点,考虑用经过它的外部路径来更新答案。
只需要在正图和反图上做 BFS 以及做前缀、后缀的
由于我们考虑的都是
随机
因此,总共随机
这部分复杂度为
清算
综上,取
根据两边常数,实际取
B=4000 较好。k 在n 比较大时直接取2 就行。
:::info[代码]
// 洛谷 P14184
// https://www.luogu.com.cn/problem/P14184
#include<bits/stdc++.h>
#define LL long long
#define bs basic_string<int>
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
mt19937 rnd(time(0));
const int N=1e5+5,B=4000,inf=0x3f3f3f3f;
int n,m,d[N],u[N],pre[N],ans[N],t,a[N],b[N],p[N];bool in[N],v[N];
struct node{int u,v;}e[N];
vector<pair<int,int>>E[N];bs G[N],_G[N];
inline void cmn(int &x,int y){x=min(x,y);}
inline void gd()
{
fill_n(d+1,n,-1);d[1]=0;
queue<int>q;q.push(1);
while(!q.empty())
{
int x=q.front();q.pop();
for(auto [y,w]:E[x]) if(d[y]==-1)
d[y]=d[x]+1,pre[y]=w,q.push(y);
}
if(d[n]==-1){while(m--) puts("-1");exit(0);}
for(int i=n;i!=1;)
{
in[pre[i]]=1;b[++t]=pre[i];
auto [u,v]=e[pre[i]];i^=u^v;a[t]=i;
}
reverse(a+1,a+1+t);reverse(b+1,b+1+t);a[++t]=n;
for(int i=1;i<=m;i++) if(!in[i])
{
auto [u,v]=e[i];
ans[i]=d[n];G[u]+=v,_G[v]+=u;
}memset(in,0,sizeof(in));
for(int i=1;i<=t;i++) in[a[i]]=1;
}
int q[N],hd,tl;
inline void bfs(int x,auto G,int *d)
{
fill_n(d+1,n,-1);hd=1,tl=0;
d[x]=0,q[++tl]=x;
while(hd<=tl)
{
int x=q[hd++];
for(int y:G[x]) if(d[y]==-1)
d[y]=d[x]+1,q[++tl]=y;
}
}
inline void sm()
{
int L=min(B,n);
for(int o=0;o<=min(L,t);o++)
{
hd=1,tl=0;
for(int i=1;i<=n;i++) v[i]=0,d[i]=inf;
int nw=-1e9,T=1;
while(hd<=tl||T<=t)
{
if(hd>tl||d[q[hd]]>nw)
{
while(T<=t&&(T-1)%(L+1)!=o) v[a[T++]]=1;
if(T<=t)
{
d[a[T]]=T-1;int x=a[T++];
nw=d[x]+L;q[++tl]=x;v[x]=1;
}
}
if(hd>tl) continue;int x=q[hd++];
if(d[x]>nw) continue;//nw 做限制
for(int y:G[x]) if(!v[y]) d[y]=d[x]+1,v[y]=1,q[++tl]=y;
}
for(int i=t;i;i--) if((i-1)%(L+1)!=o)
{
int u=a[i],v=(i<t)?a[i+1]:0;
if(i%(L+1)!=o&&v) d[u]=min(d[u],d[v]-1);
cmn(ans[b[i-1]],d[u]+t-i);
}//后缀 min 贡献
}
}//多源 bfs
inline void bg()
{
int T=2*log(n)*n/B;
iota(p,p+1+n,0);shuffle(p+1,p+1+n,rnd);
for(int i=1,x=p[1];i<=T;x=p[++i])
{
bfs(x,_G,d),bfs(x,G,u);fill_n(pre,t,inf);
for(int i=1;i<t;i++){
pre[i]=pre[i-1];
if(~d[a[i]]) cmn(pre[i],d[a[i]]+i-1);
}int mn=inf;
for(int i=t;i>1;i--)
{
if(~u[a[i]]) cmn(mn,u[a[i]]+t-i);
cmn(ans[b[i-1]],mn+pre[i-1]);
}
}
}//大的
int main()
{
cin.tie(0)->sync_with_stdio(0);cout.tie(0);cin>>n>>m;
for(int i=1,u,v;i<=m;i++) cin>>u>>v,e[i]={u,v},E[u].push_back({v,i});
memset(ans,inf,sizeof(ans));gd();sm();bg();
for(int i=1;i<=m;i++) cout<<(ans[i]>=n?-1:ans[i])<<"\n";
return 0;
}
:::