【NOIP2012】 疫情控制

【NOIP2012】 疫情控制

标签: 倍增 贪心 二分答案 NOIP


Description

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

Input
第一行一个整数 n,表示城市个数。
接下来的 n-1 行,每行 3 个整数,u、v、w,每两个整数之间用一个空格隔开,表示从 城市 u 到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。
接下来一行一个整数 m,表示军队个数。
接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎 的城市的编号。

Output
共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。

Sample Input
4
1 2 1
1 3 2
3 4 3
2
2 2

Sample Output
3

Hint
样例说明:
第一支军队在 2 号点设立检查点,第二支军队从 2 号点移动到 3 号点设立检查点,所需 时间为 3 个小时。
【数据范围】
保证军队不会驻扎在首都。
对于 20%的数据,2≤ n≤ 10;
对于 40%的数据,2 ≤n≤50,0<w <10^5;
对于 60%的数据,2 ≤ n≤1000,0<w <10^6;
对于 80%的数据,2 ≤ n≤10,000;
对于 100%的数据,2≤m≤n≤50,000,0<w <10^9


题意

大概意思就是给你n个军队(在不同的城市),你要移动他们到节点上(非根节点)来隔断根节点与每个叶子节点,求移动军队时间的最小值(两个不同的军队可以同时移动)。


题解

  • 首先,移动军队的时间肯定是越多越好,肯定是满足单调性的,我们很容易想到用二分答案来做。
  • 当我们二分答案x之后,我们应该思考军队往哪里走更好,显然是要往上走的,因为每往上走,就能覆盖更大的子树。
  • 对于往上走,我们分两种情况讨论
    1. 如果该军队没有到达根节点,那么我们不用管他,直接让他在这个节点。
    2. 如果该军队到了根节点,那么我们就可以把这个军队往下走一步,来覆盖其他节点。
  • 我们只要把军队还剩下的移动距离和每被覆盖的深度为2的点分别排序,然后扫一遍就行了。
  • 真的这么简单吗?显然不是。如果这么做的话,你会发现样例都过不了。
  • 因为即使某个军队到了根节点,他可能不如只到根节点之前的那个点优秀(因为他可能回不去了),那么我们只需要把回不去的军队弄回去就行了。
  • 对于这个结论其实很好证明:假设该军队为A,他之前的那个点为lst(A),现在他不回去,而选择另外一个点b,去他之前的那个点的军队为B,那么val (A)<val(B),就不如A回去,B再到另外的点。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
inline int read()
{
	int sum=0,p=1;char ch=getchar();
	while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
	if(ch=='-')p=-1,ch=getchar();
	while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
	return sum*p;
}
 
const int maxn=5e5+20;
 
struct node {
	int v,next,w;
};
node e[maxn*2];
int n,m;
int start[maxn],cnt;
 
void addedge(int u,int v,int w)
{
	e[++cnt]={v,start[u],w};
	start[u]=cnt;
}
 
int p[maxn][17],pw[maxn][17],deep[maxn],vis[maxn],st[maxn];
 
void dfs(int u,int fa)
{
	deep[u]=deep[fa]+1;p[u][0]=fa;
	EREP(i,u)
	{
		int v=e[i].v;
		if(v==fa)continue;
		pw[v][0]=e[i].w;
		dfs(v,u);
	}
	
}
 
int Sum=0;
 
void init()
{
	n=read();
	REP(i,1,n-1)
	{
		int u=read(),v=read(),w=read();
		addedge(u,v,w);Sum+=w;
		addedge(v,u,w);
	}
	dfs(1,0);
	for(int j=1;(1<<j)<=n;j++)
	{
		REP(i,1,n)
			p[i][j]=p[p[i][j-1]][j-1],pw[i][j]=pw[i][j-1]+pw[p[i][j-1]][j-1];
	}
	m=read();
	REP(i,1,m)st[i]=read();
}
 
bool dfs1(int u,int fa)
{
	if(vis[u])return true;
	bool flag=false;
	EREP(i,u)
	{
		int v=e[i].v;
		if(v==fa)continue;
		flag=true;
		if(!dfs1(v,u))return false;
	}
	vis[u]=flag;
	return flag;
}
 
struct army {
	int use,val,bel,nxt;
};
army a[maxn];
int start_a[maxn];
bool cmp(const army a,const army b)
{
	if(a.use==b.use)
		return a.val<b.val;
	return a.use<b.use;
}
 
bool check(int x)
{
	int army_tot=0,army_lst,uncow[maxn]={0},uncow_cnt=0;
	memset(a,0,sizeof(a));
	memset(vis,0,sizeof(vis));
	memset(start_a,0,sizeof(start_a));
	REP(i,1,m)
	{
		int u=st[i],now=0;
		DREP(j,16,0)if(p[u][j] && now+pw[u][j]<=x)
		{
			if(j==0)army_lst=u;
			now+=pw[u][j];
			u=p[u][j];
		}
		if(u!=1)vis[u]=1;
		else
		{
			army_tot++;
			a[army_tot]={0,x-now,army_lst,start_a[army_lst]};
			start_a[army_lst]=army_tot;
		}
	}
	uncow_cnt=0;
	EREP(i,1)
	{
		int v=e[i].v,mn=e[i].w,mn_j;
		if(dfs1(v,1))continue;
		for(int j=start_a[v];j;j=a[j].nxt)
			if(a[j].val<mn)
			{
				mn=a[j].val;
				mn_j=j;
			}
		if(mn<e[i].w)a[mn_j].use=1;
		else uncow[++uncow_cnt]=pw[v][0]; 
	}
	sort(uncow,uncow+1+uncow_cnt);
	sort(a+1,a+army_tot+1,cmp);
	int j=1;
	REP(i,1,uncow_cnt)
	{
		while(a[j].val<uncow[i] && !a[j].use && j<=army_tot)j++;
		if(j>army_tot || a[j].use)return false;
		j++;
	}
	return true;
}
 
void doing()
{
	int l=1,r=Sum;
	while(l<r)
	{
		int mid=(l+r)>>1;
		if(check(mid))r=mid;
		else l=mid+1;
	}
	cout<<l<<endl;
}
 
int main()
{
	init();
	doing();
	return 0;
}
 
posted @ 2017-08-01 10:13  Deadecho  阅读(297)  评论(0编辑  收藏  举报