【51nod1757】大灾变

Description

原题链接
有一棵树,树上有些特殊点,每个不是特殊点的点有一个人,每个人走过一条边的时间为1。现在所有的人要走到特殊点上,每个点可以有多个人,但每条边每1时间只能走一个人。求所有人到达特殊点的最小时间。

Solution

考虑每个人应该走向哪个洞穴。假如一个人要到一个洞穴\(a_i\),到达距离为\(d\),把每个洞穴拆成\(n\)个点,如果还没有人要走到这个洞穴的第\(d\)号点,那么这个人可以走到这个点,如果已经被另一个人所走到,则可以匹配该洞穴的下一个点或下一个洞穴。要使所有洞穴被走到编号的最大值最小。

于是可以二分答案,于是转换成判定性问题,网络流或者匈牙利判一下有没有匹配即可。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
#define rep(i,x) for(int i=ls[x];i;i=nx[i])
using namespace std;
const int N=2010,maxn=1e5+10,M=4e5+10,inf=1e9;
int to[M],nx[M],ls[maxn],vl[M],num=1;
int S,T;
int a[N];
int n,m;
bool bz[N];
void link(int u,int v,int w){
	to[++num]=v,nx[num]=ls[u],ls[u]=num;
	vl[num]=w;
	to[++num]=u,nx[num]=ls[v],ls[v]=num;
	vl[num]=0;
}
vector<int> e[N];
int d[N][N],now;
void dfs(int x,int fr,int t){
	d[now][x]=t;
	int o=e[x].size();
	fo(i,0,o-1) if(e[x][i]!=fr) dfs(e[x][i],x,t+1);
}
int h[maxn],q[maxn];
bool bfs(){
	int l=0,r=1;
	memset(h,0,sizeof(h));
	h[q[1]=S]=1;
	while(l<r){
		int x=q[++l];
		rep(i,x){
			if(!vl[i]) continue;
			int v=to[i];
			if(!h[v]) h[v]=h[x]+1,q[++r]=v;
		}
	}
	return h[T]>0;
}
int flow(int x,int t){
	if(x==T) return t;
	int fl=t;
	rep(i,x){
		int v=to[i];
		if(!vl[i] || h[v]!=h[x]+1) continue;
		int tmp=flow(v,min(t,vl[i]));
		t-=tmp,vl[i]-=tmp,vl[i^1]+=tmp;
		if(!t) break;
	}
	if(fl==t) h[x]=-1;
	return fl-t;
}
bool check(int x){
	memset(ls,0,sizeof(ls));
	S=n+x*m+1,T=S+1;
	num=1;
	fo(i,1,m)
	fo(j,1,x){
		link(i+n+(j-1)*m,T,1);
		if(j<x) link(i+n+(j-1)*m,i+n+j*m,inf);
	}
	fo(i,1,n) if(!bz[i]){
		link(S,i,1);
		fo(j,1,m) if(d[i][a[j]]<=x) link(i,j+n+(d[i][a[j]]-1)*m,1);
	}
	int ans=0;
	while(bfs()) ans+=flow(S,inf);
	return ans==n-m;
}
int main()
{
	scanf("%d %d",&n,&m);
	fo(i,2,n){
		int u,v;
		scanf("%d %d",&u,&v);
		e[u].push_back(v),e[v].push_back(u);
	}
	fo(i,1,n) now=i,dfs(i,0,0);
	fo(i,1,m) scanf("%d",&a[i]),bz[a[i]]=1;
	int l=1,r=n;
	while(l+1<r){
		int mid=(l+r)>>1;
		check(mid)?r=mid:l=mid;
	}
	if(check(l)) r=l;
	printf("%d\n",r);
}

posted @ 2018-05-25 21:32  sadstone  阅读(125)  评论(0编辑  收藏  举报