洛谷 P1084 疫情控制

图论
摘要由CSDN通过智能技术生成

题目描述

H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,

也是树中的根节点。

H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境

城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境

城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,

首都是不能建立检查点的。

现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在

一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等

于道路的长度(单位:小时)。

请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。

输入输出格式

输入格式:

第一行一个整数 n,表示城市个数。

接下来的 n-1 行,每行 3 个整数,u、v、w,每两个整数之间用一个空格隔开,表示从

城市 u 到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。

接下来一行一个整数 m,表示军队个数。

接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎

的城市的编号。

输出格式:

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

输入输出样例

输入样例#1:
4 
1 2 1 
1 3 2 
3 4 3 
2 
2 2
输出样例#1:
3

说明

【输入输出样例说明】

第一支军队在 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。

NOIP 2012 提高组 第二天 第三题


真是noip神题,做了我一天(一定是我太弱了)。
说一下思路吧。
因为并不知道最小值如何直接求,而时间这个量明显符合单调性,考虑二分。
给定一个限制时间,判断在这个时间里,所有的军队是否可以全部到位,转化成可行性问题。
考虑一支军队在树上爬,一定是越靠近根越优,(最好到根,然而并不可以)。
令所有可以到达首都的军队到达根,无法到达的就爬到距离根尽量近的地方。
从根开始dfs,找到没有被军队占领的第二层节点(贪心),这时就有一个问题,可以让其他的军队下来,或让原来这条链上的军队下来。继续贪心,将需要的时间和结余的时间分别排序,对于每支时间剩余的军队,先看他可不可以回到需要时间更多的链上,否则就去需要时间最小的节点。

代码么,比较凌乱,大家看懂思路还是自己写吧。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
using namespace std;
const int N=50005;
struct node
{
	int ori;
	long long val;
}tim[N],tmp;
vector<node>nd;
int n,m,cnt,hd[N],arm[N],dep[N],f[N][21],pre[N];
long long dis[N];
bool b[N],bb[N];
struct edge
{
	int to,nxt;
	long long val;
}v[2*N];
void addedge(int x,int y,long long z)
{
	++cnt;
	v[cnt].to=y;
	v[cnt].nxt=hd[x];
	v[cnt].val=z;
	hd[x]=cnt;
}
bool cmp(node c,node d)
{
	return c.val<d.val;
}
int cb(int u)
{
	for(int i=15;i>=0;i--)
		if(dep[f[u][i]]>1)
			u=f[u][i];
	return u;
}
void build(int u,int fa)
{
	for(int i=hd[u];i;i=v[i].nxt)
		if(v[i].to!=fa)
		{
			f[v[i].to][0]=u;
			dis[v[i].to]=dis[u]+v[i].val;
			dep[v[i].to]=dep[u]+1;
			build(v[i].to,u);
		}
}
bool dfs(int u,int fa)
{
	if(u==1)
	{
		bool flg=0;
		for(int i=hd[1];i;i=v[i].nxt)
			if(!dfs(v[i].to,u))
			{
				bb[v[i].to]=1;
				tmp.ori=v[i].to,tmp.val=dis[v[i].to];
				nd.push_back(tmp);
				flg=1;
			}
		if(flg)
			return 0;
		return 1;
	}
	if(b[u])
		return 1;
	bool flg=0;
	for(int i=hd[u];i;i=v[i].nxt)
		if(v[i].to!=fa)
			if(!dfs(v[i].to,u))
				return 0;
			else
				flg=1;
	if(flg)
		return 1;
	return 0;
}
bool pd(long long lim)
{
	nd.clear();
	int cnt=0;
	memset(tim,0,sizeof(tim));
	memset(b,0,sizeof(b));
	memset(bb,0,sizeof(bb));
	for(int i=1;i<=m;i++)
	{
		int t=arm[i];
		long long lft=lim;
		for(int j=16;j>=0;j--)
			if(dep[f[t][j]]>0&&dis[t]-dis[f[t][j]]<=lft)
				lft-=dis[t]-dis[f[t][j]],t=f[t][j];
		if(t==1)
			tim[++cnt].val=lft,tim[cnt].ori=arm[i];
		else
			b[t]=1;
	}
	if(dfs(1,0))
		return 1;
	sort(tim+1,tim+cnt+1,cmp);
	sort(nd.begin(),nd.end(),cmp);
	int k=0;
	for(int i=1;i<=cnt;i++)
	{
		if(bb[pre[tim[i].ori]])
			bb[pre[tim[i].ori]]=0;
		else
		{
			while(k<nd.size()&&!bb[nd[k].ori])
				k++;
			if(nd[k].val<=tim[i].val)
				bb[nd[k].ori]=0;
		}
	}
	for(int i=0;i<=nd.size()-1;i++)
		if(bb[nd[i].ori])
			return 0;
	return 1;
}
int main()
{
	scanf("%d",&n);
	long long l=0,r=0,mid;
	for(int i=1;i<=n-1;i++)
	{
		int x,y;
		long long z;
		scanf("%d%d%lld",&x,&y,&z);
		addedge(x,y,z);
		addedge(y,x,z);
		r+=z;
	}
	dep[1]=1;
	build(1,0);
	for(int i=1;i<=16;i++)
		for(int j=1;j<=n;j++)
			f[j][i]=f[f[j][i-1]][i-1];
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&arm[i]);
		pre[arm[i]]=cb(arm[i]);
	}
	while(l<r)
	{
		mid=(l+r)/2;
		if(!pd(mid))
			l=mid+1;
		else
			r=mid;
	}
	if(pd(l))
		printf("%lld\n",l);
	else
		printf("-1\n");
	return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值