【NOIP2012】疫情控制

题目描述

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 提高组 第二天 第三题




题解

首先,这是一棵树,我们要移动军队封锁所有子树,那么军队一定是向上移动的。
其次,我们要求出的时间是所有军队移动的最大时间,属于最大值最小问题,用二分
再来,要将军队向上移动,用倍增加速

若M<degree(N)直接无解
否则一定有解

这样一来,我们大概有了思路:
1、二分最大时间
2、check
O(nlogn)时间预处理出第i个节点向上2^j代祖先f[i][j],每次O(mlogn)时间向上移动直至根节点前
这时候就有一个问题了,军队有可能跨过根节点到达别的子树去,这个时候怎么办?
这个时候我们分两种情况讨论:
1、对于到不了根节点的军队,向上停在能到达的最高位置
2、对于能到达根节点的军队,先暂不处理,保存起来
我们先从根节点花费O(n)进行一次dfs,求出所有没有被封锁的子树,尝试用到达根节点的军队逐个封锁
封锁过程:
将军队按剩余时间从大到小排序,将剩余子树按到根路径长度进行排序,

那么每次对于最长的子树,是我们不惜代价首先要解决的,若有军队之前从那棵子树来,那么取其中 最小的没用过的子树,若没有,则使用当前剩余时间最大的军队
当且仅当子树无法封锁,此时间下无解

思路大概是这样,实现起来还要注意很多细节,要耐得住心
继续加油↖(^ω^)↗
NOIP2017   RP++

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
using namespace std;
const int maxn = 50005,maxm = 100005,INF = 2000000000;

inline int read(){
	int out = 0,flag = 1;char c = getchar();
	while (c < 48 || c > 57) {if (c == '-') flag = -1;c = getchar();}
	while (c >= 48 &&c <= 57) {out = out * 10 + c - 48;c = getchar();}
	return out * flag;
}

int N,M,army[maxn],f[maxn][20],cost[maxn][20];

int head[maxn],nedge = 0;
struct EDGE{
	int to,w,next;
}edge[maxm];

inline void build(int u,int v,int w){
	edge[nedge] = (EDGE) {v,w,head[u]};
	head[u] = nedge++;
	edge[nedge] = (EDGE) {u,w,head[v]};
	head[v] = nedge++;
}

void init(){
	memset(head,-1,sizeof(head));
	N = read();
	int a,b,w;
	for (int i = 1; i < N; i++){
		a = read();
		b = read();
		w = read();
		build(a,b,w);
	}
	M = read();
	for (int i = 1; i <= M; i++) army[i] = read();
}

int outde[maxn];

void dfs1(int u,int fa){
	f[u][0] = fa;
	for (int k = head[u]; k != -1; k = edge[k].next)
		if (edge[k].to != fa){
			outde[u]++;
			cost[edge[k].to][0] = edge[k].w;
			dfs1(edge[k].to,u);
		}
}

bool vis[maxn];

bool dfs(int u,int fa){
	if (vis[u]) return true;
	if (!outde[u]) return false;
	
	for (int k = head[u]; k != -1; k = edge[k].next)
		if (edge[k].to != fa && !dfs(edge[k].to,u))
			return false;
	return true;
}

struct node{
	int id,w;
}A[maxn],B[maxn];
int ai = 0,bi;
int restmin[maxn],resti[maxn],used[maxn];

inline bool operator < (const node& a,const node& b){
	return a.w > b.w;
}

inline bool check(int lim){
	int T = max(N,M);
	for (int i = 1; i <= T; i++){
		vis[i] = resti[i] = used[i] = 0;
	}
	used[0] = true;
	int u,w = 0,p;
	ai = bi = 0;
	for (int i = 1; i <= M; i++){
		u = army[i];
		w = 0;
		while (1){
			p = -1;
			while (f[u][p + 1] > 1 && cost[u][p + 1] + w <= lim) p++;
			if (p < 0) break;
			w += cost[u][p];
			u = f[u][p];
		}
		if (f[u][0] != 1 || cost[u][0] + w >= lim) vis[u] = true;
		else {
			B[++bi] = (node){i,lim - w - cost[u][0]};
			if (!resti[u] || restmin[u] > B[bi].w)
				restmin[u] = B[bi].w,resti[u] = i;
		}
	}
	for (int k = head[1]; k != -1; k = edge[k].next)
		if (!dfs(edge[k].to,1))
			A[++ai] = (node) {edge[k].to,edge[k].w};
	sort(A + 1,A + 1 + ai);
	sort(B + 1,B + 1 + bi);
	int j = 1;
	for (int i = 1; i <= ai; i++){
		if (!used[resti[A[i].id]]) {used[resti[A[i].id]] = true;continue;}
		while (j <= bi && (used[B[j].id] || B[j].w < A[i].w)) j++;
		if (j > bi) return false;
		used[B[j++].id] = true;
	}
	return true;
}

void solve(){
	if (M < outde[1]) {printf("-1
"); return;}
	for (int k = 1; k < 20; k++)
		for (int i = 1; i <= N; i++){
			f[i][k] = f[f[i][k - 1]][k - 1];
			cost[i][k] = cost[i][k - 1] + cost[f[i][k - 1]][k - 1];
		}
	int L = 0,R = 1000000ll;
	while (L < R){
		int mid = (L + R) >> 1;
		if (check(mid)) R = mid;
		else L = mid + 1;
	}
	printf("%d
",R);
}

int main(){
	init();
	dfs1(1,0);
	solve();
	return 0;
}


原文地址:https://www.cnblogs.com/Mychael/p/8282862.html