[vijos1783][NOIP2012]疫情控制

Description

H国有\(n\)个城市,这\(n\)个城市用\(n-1\)条双向道路相互连通构成一棵树,\(1\)号城市是首都,也是树中的根节点。
H国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在H国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。

HINT

\(2≤m≤n≤50,000,0<w<10^9\)

Solution

二分答案t.
处理出t时每个军队能到达的最高的位置(约定离根越近越高).
如果一个军队无法到达首都,则这个军队在最高的位置是最优的(能在尽量多叶子到根的路径上).
如果一个军队能到达首都(记为点集X),那么它有两种选择:留在其到根路径上与根相邻的点u;到达另一个与根相邻的点.处理出其到根后还能走多久,以及唯一匹配的点u.
处理出还需覆盖的与根相邻的点(记为点集Y).
将X,Y按边权排序,贪心即可.

#define K 17
#define N 50005
typedef long long ll;
struct graph{
	int nxt,to;ll w;
}e[N<<1];
struct rest{
	ll w;int x;
}a1[N],a2[N];
ll len[N],w[N][K],l,r,mid;
int f[N][K],nxt[N],g[N],dep[N],p[N],n,m,t1,t2,cnt;
stack<int> s,s1;
bool b[N],c[N];
bool operator < (rest x,rest y){
	return x.w<y.w;
}
inline void addedge(int x,int y,ll w){
	e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;e[cnt].w=w;
}
inline void dfs(int u){
	dep[u]=1;s.push(u);
	while(!s.empty()){
		u=s.top();s.pop();
		if(u==1) for(int i=0;i<K;++i)
			f[u][i]=1;
		else for(int i=1;i<K;++i){
			f[u][i]=f[f[u][i-1]][i-1];
			w[u][i]=w[u][i-1]+w[f[u][i-1]][i-1];
		}
		for(int i=g[u],c;i;i=e[i].nxt)
			if(!dep[c=e[i].to]){
				if(u==1) ++cnt,nxt[c]=c;
				else nxt[c]=nxt[u];
				dep[c]=dep[u]+1;
				f[c][0]=u;
				w[c][0]=e[i].w;
				len[c]=len[u]+e[i].w;
				s.push(c);
			}
	}
}
inline bool dfs2(int u){
	if(c[u]) return true;
	for(int i=g[u];i;i=e[i].nxt)
		if(dep[e[i].to]>dep[u]){
			c[u]=true;break;
		}
	for(int i=g[u];i;i=e[i].nxt)
		if(dep[e[i].to]>dep[u]){
			if(!dfs2(e[i].to)) c[u]=false; 
		}
	return c[u];
}
inline bool chk(ll k){
	memset(c,0,sizeof(c));
	ll x;t1=t2=0;
	for(int i=1,j,u;i<=m;++i){
		u=p[i];x=k;
		if(x>len[u])
			a1[++t1].w=x-len[u],a1[t1].x=nxt[u];
		else while(x){
			for(j=K-1;j>=0&&(w[u][j]>x||f[u][j]==1);--j);
			if(j<0){
				c[u]=true;break;
			}
			x-=w[u][j];u=f[u][j];
			if(!x) c[u]=true;
		}
	}
	dfs2(1);
	memset(b,0,sizeof(b));
	for(int i=g[1];i;i=e[i].nxt)
		if(!c[e[i].to])
			a2[++t2].w=e[i].w,a2[t2].x=e[i].to,b[e[i].to]=true;
	sort(a1+1,a1+1+t1);
	sort(a2+1,a2+1+t2);
	for(int i=1,j=1;i<=t1;++i){
		if(b[a1[i].x]){
			b[a1[i].x]=false;continue;
		}
		while(j<=t2&&!b[a2[j].x]) ++j;
		if(j>t2) return true;
		if(a1[i].w>=a2[j].w) b[a2[j].x]=false;
	}
	for(int i=1;i<=t2;++i)
		if(b[a2[i].x]) return false; 
	return true;
}
inline void Aireen(){
	n=read();
	int x,y;ll z;
	for(int i=1;i<n;++i){
		x=read();y=read();z=1ll*read();r+=z;
		addedge(x,y,z);addedge(y,x,z);
	}
	m=read();
	for(int i=1;i<=m;++i)
		p[i]=read();
		
	cnt=0;dfs(1);
	if(cnt>m){
		puts("-1");return;
	}
	while(l<r){
		mid=l+r>>1;
		if(chk(mid)) r=mid;
		else l=mid+1ll; 
	}
	printf("%d\n",l);
}
int main(){
	Aireen();
	return 0;
}

2017-10-27 13:47:52

posted @ 2021-11-25 14:07  Aireen_Ye  阅读(43)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.