【NOIP2012】疫情控制(神仙贪心)

【NOIP2012】疫情控制

时限: 1 Sec  内存: 128 MB

题目描述

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

输入格式

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


保证军队不会驻扎在首都。 
对于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

输出格式

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

输入样例

4 
1 2 1 
1 3 2 
3 4 3 
2 
2 2 

输出样例

3

 

 

 

题解

神仙贪心。。。。

我们明显可以二分答案

然后贪心地想一下,就觉得每个军队都应该尽量往上爬,这样他们控制的节点才更多

然后从上到下dfs一遍,看根节点能不能走到叶子节点

这样就结束了???

真正的贪心现在才开始

我们可以发现一个巨大的问题:根节点不能建检查点

那怎么办???

我们先把所有能走到根节点的军队集合在一起,按他们的剩余时间排个序

然后依次分配给那些没有被完全控制的根节点的儿子(把儿子也按剩余时间也排个序,大的分大的,小的分小的)

我们又可以发现一个巨大的问题:

一些军队原本可以控制自己上来的子树,但是这条边又很长,他爬上根节点就回不去了,其他的军队也无法帮助他控制

所以我们对于那些(如果(没有可以到达根节点的军队来控制)就(无法被控制的根节点的儿子)),要预留一个无法走一个来回的军队,且他的剩余时间最小

然后把这些出去这种情况的根节点儿子再拿出来匹配

就可以了

代码:(代码丑勿喷)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 50005
#define LL long long
int fir[N],to[2*N],nxt[2*N],cd[2*N],d[N],cnt;
void adde(int a,int b,int c)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;cd[cnt]=c;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;cd[cnt]=c;
	d[a]++;d[b]++;
}
#define LOG 18
int n,m,pos[N],f[N][LOG+2];
LL dis[N],l,mid,r;
void predfs(int u)
{
	for(int v,p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(v!=f[u][0]){
			f[v][0]=u;
			dis[v]=dis[u]+1ll*cd[p];
			r=max(dis[v],r);
			predfs(v);
		}
	}
}
struct node{
	int id;LL dis;
}a[N];
bool cmp(node q,node w){return q.dis<w.dis;}
int acnt;
bool flg[N],vis[N];
void dfs(int u)
{
	bool sf=1,ff=0;
	for(int v,p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(v!=f[u][0]){
			dfs(v);ff=1;
			sf&=flg[v];
		}
	}
	if(ff==1)flg[u]|=sf;
}
int len[N],lencnt;
bool check()
{
	acnt=0;lencnt=0;
	memset(flg,0,sizeof(flg));
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=m;i++){
		int x=pos[i];
		for(int j=LOG;j>=0;j--)
			if(f[x][j]>1&&dis[pos[i]]-dis[f[x][j]]<=mid)
				x=f[x][j];
		if(f[x][0]==1&&dis[pos[i]]-dis[1]<=mid){
			a[++acnt].id=x;
			a[acnt].dis=mid-dis[pos[i]];
		}
		else flg[x]=1;
	}
	dfs(1);sort(a+1,a+acnt+1,cmp);
	for(int i=1;i<=acnt;i++)
		if(!flg[a[i].id]&&a[i].dis<dis[a[i].id])
			flg[a[i].id]=1,vis[i]=1;
	for(int p=fir[1];p;p=nxt[p])
		if(!flg[to[p]])
			len[++lencnt]=cd[p];
	sort(len+1,len+lencnt+1);
	for(int i=acnt,j=lencnt;j>=1;j--){
		while(i>=1&&vis[i])i--;
		if(!vis[i]&&a[i].dis<len[j])
			return 0;
		vis[i]=1;
	}
	return 1;
}
int main()
{
	//freopen("testdata.in","r",stdin);
	int i,j,u,v,w;
	n=gi();
	for(i=1;i<n;i++){
		u=gi();v=gi();w=gi();
		adde(u,v,w);
	}
	m=gi();
	for(i=1;i<=m;i++)
		pos[i]=gi();
	if(d[1]>m){printf("-1");return 0;}
	predfs(1);
	for(j=1;j<=LOG;j++)
		for(i=1;i<=n;i++)
			f[i][j]=f[f[i][j-1]][j-1];
	r=10000000000000ll;
	while(l<r){
		mid=(l+r)>>1;
		if(check())r=mid;
		else l=mid+1;
	}
	printf("%lld",l);
}

 

 

 

注意二分的范围不能是最深节点的深度,开大一点

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值