洛谷 P1084

题目链接: P1084 疫情控制

题目大意

一棵带权的有根树, 有 \(m\) 个军队, 对于每个军队,移动到起距离不超过 \(k\) 且不为根节点的一个点, 使得最终根节点与原来的所有叶子的路径上都至少有一个军队, 求最小的 \(k\)

大概就是这意思吧

solution

参考自 大佬

诶, 求最大值最小? 二分答案

\(check\) 怎么写呢? 当然是 \(check\) 时间了

那我们可以往贪心的思路想一想.

显然,我们知道, 军队越靠近根节点越好, 为什么呢?

假设我们往下走, 会发现需要多一个军队,且本来可以不动,为啥要往下走呢?

所以我们要让军队尽可能的往上走, 然后到根节点的时候,暂时停在根节点. 否则走到时间限制内能后走到的神队最小的点

那我们怎么继续处理细节呢?

我们先找出以根为根的子树, 如果在这棵子树中, 有到叶子节点中还未被驻扎的路径, 那我们记录一下这棵子树的根节点

那这几个节点怎么办呢? 那肯定是其余军队转移过来啊!

对于其他节点, 啃腚是剩余时间最少的军队驻扎在该节点, 其余的前往根节点啊.

对于那些还未驻扎的节点, 也是剩余时间最少的军队驻扎在该节点最优, 那我们按照这个排个序就好了

然后将在根节点的军队和未被驻扎的军队匹配, 如果仍有节点未被驻扎, 那这个时间限制不行. 反之, 则可以

到这,我们的题目就分析完了(好累)

还有一些细枝末节, 那就请看代码注释吧

code:

/**
*    Author: Alieme
*    Data: 2020.8.26
*    Problem: Luogu P1084
*    Time: O()
*/
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>

#define int long long
#define rr register

#define inf 1e9
#define MAXN 100010

using namespace std;

inline int read() {
	int s = 0, f = 0;
	char ch = getchar();
	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

void print(int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) print(x / 10);
	putchar(x % 10 + 48);
}

struct Edge {
	int nxt;
	int to;
	int val;
	Edge() {}
	Edge(int Nxt, int To, int Val) {nxt = Nxt, to = To, val = Val;}
}e[MAXN];

struct Node {
	int dis;
	int node;
	Node() {}
	Node(int Dis, int ode) { dis = Dis, node = ode;}
	void clear() {dis = 0, node = 0;}
	bool operator < (const Node &b) const {return dis < b.dis;}
}h[MAXN];        // 存点和剩余时间

int n, m, tot;

int head[MAXN], army[MAXN], dep[MAXN], need[MAXN], ned[MAXN], tim[MAXN];

int f[MAXN][30], dis[MAXN][30];

bool stand[MAXN];

inline void add(int from, int to, int val) {
	e[++tot] = Edge(head[from], to, val);
	head[from] = tot;
}

void dfs_init(int x) {   // 倍增预处理
	for (rr int i = head[x]; i; i = e[i].nxt) {
		int to = e[i].to;
		if (dep[to]) continue;
		dep[to] = dep[x] + 1;
		f[to][0] = x;
		dis[to][0] = e[i].val;
		for (rr int j = 1; j <= 20; j++) {
			f[to][j] = f[f[to][j - 1]][j - 1];
			dis[to][j] = dis[to][j - 1] + dis[f[to][j - 1]][j - 1];
		}
		dfs_init(to);
	}
}

bool dfs(int x) {        // 查找路径是否有军队驻扎
	bool t = 0;
	if (stand[x]) return 1;
	for (rr int i = head[x]; i; i = e[i].nxt) {
		int to = e[i].to;
		if (dep[to] < dep[x]) continue;
		t = 1;
		if (!dfs(to)) return 0;
	}
	if (!t) return 0;
	return 1;
}

inline bool check(int mid) {
	memset(stand, 0, sizeof stand);
	memset(tim, 0, sizeof tim);
	memset(need, 0, sizeof need);
	memset(ned, 0, sizeof ned);
	for (rr int i = 1; i <= n; i++) h[i].clear();
	int tot = 0, cnt = 0, opt = 0;       // 初始化

	for (rr int i = 1; i <= m; i++) {
		int x = army[i], dist = 0;
		for (rr int j = 20; j >= 0; j--) 
			if (f[x][j] > 1 && dist + dis[x][j] <= mid) {
				dist += dis[x][j];
				x = f[x][j];
			}
		// cout << dist << " " << dis[x][0] << " ";
		if (f[x][0] == 1 && dist + dis[x][0] <= mid) h[++tot] = Node(mid - dist - dis[x][0], x);
		else stand[x] = 1;
	}     // 往上跳

	for (rr int i = head[1]; i; i = e[i].nxt)       // 查找军队
		if (!dfs(e[i].to))
			need[e[i].to] = 1;
	sort(h + 1, h + 1 + tot);
	// cout << tot << " ";
	for (rr int i = 1; i <= tot; i++) 
		if (need[h[i].node] && h[i].dis < dis[h[i].node][0]) need[h[i].node] = 0;
		else tim[++opt] = h[i].dis;
	for (rr int i = head[1]; i; i = e[i].nxt)
		if (need[e[i].to])
			ned[++cnt] = dis[e[i].to][0];
	// cout << cnt << " " << opt << " " << mid << "\n";
	if (cnt > opt) return 0;
	sort(tim + 1, tim + opt + 1);
	sort(ned + 1, ned + cnt + 1);
	int i = 1, j = 1;
	while (i <= cnt && j <= opt) 
		if (tim[j] >= ned[i]) i++, j++;
		else j++;
	if (i > cnt) return 1;
	else return 0;
}

signed main() {
	n = read();
	for (rr int i = 1; i < n; i++) {
		int u = read();
		int v = read();
		int w = read();
		add(u, v, w);
		add(v, u, w);
	}
	m = read();
	for (rr int i = 1; i <= m; i++) army[i] = read();
	dep[1] = 1;
	dfs_init(1);
	int l = 0, r = inf;
	bool use = 0;
	while (l < r - 1) {
		int mid = (l + r) >> 1;
		if (check(mid)) {
			r = mid;
			use = 1;
		}
		else l = mid;
	}
	if (!use) print(-1);
	else print(r);
}	
posted @ 2020-08-26 14:51  Aliemo  阅读(158)  评论(0编辑  收藏  举报