题解【CF852D Exploration plan】
传送门。
$\texttt{Description}$
给你 $v$ 个点 $e$ 条边的带权无向图,$n$ 个人位于若干个点上且可以地走,最少多长时间至少有 $m$ 个点上至少有一个人。
$\texttt{Solution}$
瞄了一眼题解才知道可以二分做。
直接做是不好做的,但是观察可得答案具有单调性,于是可以二分答案。
考虑二分一个 $k$,表示每个人的行走时间都不大于 $k$。
这样我们可以通过这个 $k$ 来建图,对于每个人与能走到的点连一条边。
连出来的图就是一个二分图了,左边是人,右边是图上的点,需要求的就是二分图最大匹配,因为一个人只能走到一个点上。
这样我们建立源点 $s$ 和汇点 $t$,对于 $s$ 连向每一个人,容量为 $1$,每个点连 $t$,容量同样为 $1$,跑一遍 $\text{Dinic}$ 即可。
另外需要预处理一个 $\Theta(n^3)$ 的 $\text{Floyd}$,建图时需要判断两点的距离是否不大于 $k$。
贴一下代码:
#include#include #include #include #include #include #include #include using namespace std; inline int read() { int s=0,w=1; char ch=getchar(); while(ch<'0' or ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0' and ch<='9')s=s*10+(ch-'0'),ch=getchar(); return s*w; } const int INF=1e9+10; inline int Max(int x,int y){return x>y?x:y;} inline int Min(int x,int y){return x 0?x:-x;} const int MAXN(520000); const int MAXM(610); int V,M,N,m; int a[MAXN]; int G[MAXM][MAXM]; inline void Floyd() { for(register int k=1;k<=V;k++) for(register int i=1;i<=V;i++) for(register int j=1;j<=V;j++) G[i][j]=Min(G[i][j],G[i][k]+G[k][j]); return; } struct E{int to,nxt,flow;}; E edge[MAXN]; int head[MAXN],tot(1); int s,t; inline void add(int u,int v,int f) { edge[++tot].nxt=head[u]; head[u]=tot; edge[tot].to=v; edge[tot].flow=f; return; } inline void add_edge(int u,int v,int f) { add(u,v,f); add(v,u,0); return; } int dep[MAXN]; queue q; inline bool bfs() { memset(dep,0,sizeof(dep)); dep[s]=1; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); for(register int i=head[u];i;i=edge[i].nxt) { E e=edge[i]; if(!dep[e.to]&&e.flow) { dep[e.to]=dep[u]+1; q.push(e.to); } } } return dep[t]; } inline int dfs(int u,int in) { if(u==t) return in; int out(0); for(register int i=head[u];i&∈i=edge[i].nxt) { E e=edge[i]; if(e.flow&&dep[e.to]==dep[u]+1) { int now=dfs(e.to,Min(e.flow,in)); edge[i].flow-=now; edge[i^1].flow+=now; in-=now; out+=now; } } if(!out) dep[u]=0; return out; } inline void Clear() { tot=1; memset(head,0,sizeof(head)); memset(edge,0,sizeof(edge)); return; } inline bool check(int lim) { Clear(); int ans(0); for(register int i=1;i<=N;i++) for(register int j=1;j<=V;j++) if(G[a[i]][j]<=lim) add_edge(i,j+N,1); for(register int i=1;i<=N;i++) add_edge(s,i,1); for(register int i=1;i<=V;i++) add_edge(i+N,t,1); while(bfs()) ans+=dfs(s,INF); return ans>=m; } int main() { V=read(),M=read(),N=read(),m=read(); s=0,t=V+N+1; for(register int i=1;i<=N;i++) a[i]=read(); for(register int i=1;i<=V;i++) for(register int j=1;j<=V;j++) if(i!=j) G[i][j]=INF; for(register int i=1;i<=M;i++) { int u=read(),v=read(),w=read(); G[u][v]=G[v][u]=Min(G[u][v],w); } Floyd(); int l=0,r=10000000,res(-1); while(l<=r) { int mid=(l+r)>>1; if(check(mid)) r=mid-1,res=mid; else l=mid+1; } printf("%d\n",res); return 0; }
$$\texttt{The End.by UF}$$