[ABC397G] Maximize Distance
[ABC397G] Maximize Distance
题意
给出一个有向图,你需要选择 \(k\) 条边让它们的边权为 \(1\),剩余边的边权为 \(0\)。问 \(1\) 到 \(n\) 的最短路长度的最大值为多少。
思路
最短路最大值显然二分答案,考虑给出最短路的长度如何计算最少需要的边数。
若最短路长度等于 \(1\),则问题就变为了选择最少的边使得从 \(1\) 到 \(n\) 的每条路径都经过至少一条这些边。
设两点之间仅当边权为 \(0\) 时有边,则若 \(1\) 可以到达 \(n\),最短路长度为 \(0\),所以问题又变成了删去最少的边使得 \(1\) 不能到达 \(n\)。
于是可以建图,边的流量为 \(1\),求最小割即可。
现在将该问题扩展到最短路长度不为 \(1\) 的情况。设最短路长度为 \(x\),于是我们建立 \(x\) 层图。若第 \(i\) 层有一条 \((x_i,y_i)\) 的边,则从第 \(i\) 层向第 \(i+1\) 层连 \((x_i,y_{i+1})\) 的边,流量为无限大。再将每层的 \(n\) 号点向第一层的 \(n\) 号点连边,流量同样为无限大,求最小割即可得到答案。
考虑为什么可以这么做。设跨层的边表示将路径长度加 \(1\)。若节点 \(1\) 可以到达第 \(i\) 层的 \(n\) 号节点,则经过了 \(i-1\) 条跨层的边,于是该层的最短路长度便为 \(i-1\),显然不满足条件。所以该图的一个割一定满足第 \(i\) 层的最短路长度为 \(i\),于是求出最小割便求出了答案。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=20005,INF=0x3f3f3f3f;
struct node{
int x,w,rev,id;
};
vector<node> t[N];
int dep[N],gap[N],maxflow;
int n,m,k,S,T;
void add(int x,int y,int w){
t[x].push_back({y,w,t[y].size()});
t[y].push_back({x,0,t[x].size()-1});
}
void bfs(){
memset(gap,0,sizeof(gap));
memset(dep,-1,sizeof(dep));
queue<int> q;
q.push(T),dep[T]=0,gap[dep[T]]++;
while(!q.empty()){
int u=q.front();q.pop();
for(node v:t[u]){
if(dep[v.x]==-1){
dep[v.x]=dep[u]+1;
gap[dep[v.x]]++,q.push(v.x);
}
}
}
}
int dfs(int x,int flow){
if(x==T){
maxflow+=flow;
return flow;
}
int used=0;
for(int i=0;i<t[x].size();i++){
int v=t[x][i].x;
if(t[x][i].w&&dep[v]+1==dep[x]){
int mn=dfs(v,min(t[x][i].w,flow-used));
if(mn) t[x][i].w-=mn,t[v][t[x][i].rev].w+=mn,used+=mn;
if(flow==used) return used;
}
}
gap[dep[x]]--;
if(!gap[dep[x]]) dep[S]=n*k+1;
dep[x]++,gap[dep[x]]++;
return used;
}
int isap(){
maxflow=0;
bfs();
while(dep[S]<n*k) dfs(S,INF);
return maxflow;
}
struct IN{
int x,y;
}in[105];
bool check(int mid){
for(int i=1;i<=n*k;i++)
t[i].clear();
for(int d=0;d<mid;d++){
for(int i=1;i<=m;i++){
add(d*n+in[i].x,d*n+in[i].y,1);
if(d!=mid-1) add(d*n+in[i].x,(d+1)*n+in[i].y,INF);
}
if(d) add(d*n+n,n,INF);
}
return isap()<=k;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
cin>>n>>m>>k;
S=1,T=n;
for(int i=1;i<=m;i++)
cin>>in[i].x>>in[i].y;
int l=1,r=k,mid,res=0;
while(l<=r){
mid=(l+r)/2;
if(check(mid))
l=mid+1,res=mid;
else
r=mid-1;
}
cout<<res;
return 0;
}

浙公网安备 33010602011771号