P1084 [NOIP2012 提高组] 疫情控制

P1084 NOIP2012提高组 疫情控制

先二分

  1. 将所有军队尽量往上跳
  2. 如果能跳到根节点的子节点且能够越过根节点,存入pair(到根节点后的剩余时间,节点编号)(使用倍增优化) ; 否则:驻扎此节点
  3. 将pair的点按第一个排序
  4. 用第一个最小覆盖第一个没有被覆盖的根节点的子节点(贪心)
  5. 剩下的节点排序后使用双指针判断是否有解
点击查看代码

#include <stdio.h>
#include <string.h>
#include <algorithm>
const int N = 5e4 + 5, M = N << 1, logN = 16;
typedef long long LL;
int n, m;
int h[N], e[M], w[M], nxt[M], idx;
int f[N][logN], dep[N];
LL dist[N][logN];
int armies[N];
void add(int a, int b, int c) {
	e[++ idx] = b, w[idx] = c, nxt[idx] = h[a], h[a] = idx;
}
void dfs1(int u) {
	for(int i = 1; i < logN && f[u][i-1]; i ++)
		f[u][i] = f[f[u][i-1]][i-1], dist[u][i] = dist[f[u][i-1]][i-1] + dist[u][i-1];
	for(int i = h[u]; i; i = nxt[i]) {
		int v = e[i];
		if(v == f[u][0]) continue;
		f[v][0] = u, dep[v] = dep[u] + 1, dist[v][0] = w[i], dfs1(v);
	}
}
bool cov[N]; // 节点是否驻扎
bool need_cov[N]; // 节点(根节点的儿子)需要被驻扎
std::pair<LL, int> remain[N]; // 能够到达根节点的军队的(剩余时间,节点编号) 其余的直接使用
LL val1[N]; // 没有匹配的军队所有距离
LL val2[N]; // 没有匹配的点到根节点的距离
bool dfs2(int u) { // 子树是否全部驻扎
	if(cov[u]) return true;
	bool is_leaf = true; // 是否为叶节点
	for(int i = h[u]; i; i = nxt[i]) {
		int v = e[i];
		if(v == f[u][0]) continue;
		is_leaf = false;
		if(!dfs2(v)) return false;
	}
	return !is_leaf;
}
bool check(LL lim) {
	memset(cov + 1, 0, n);
	int tot = 0, tot1 = 0, tot2 = 0;
	for(int i = 1; i <= m; i ++) {
		int u = armies[i]; LL now = 0;
		for(int j = logN - 1; j >= 0; j --)
			if(f[u][j] > 1 && now + dist[u][j] <= lim) now += dist[u][j], u = f[u][j];
		if(f[u][0] == 1 && now + dist[u][0] <= lim) remain[tot ++] = {lim - now - dist[u][0], u};
		else cov[u] = true; // 原地不动
	}
	for(int i = h[1]; i; i = nxt[i]) need_cov[e[i]] = !dfs2(e[i]);
	std::sort(remain, remain + tot);
	for(int i = 0; i < tot; i ++)
		if(need_cov[remain[i].second] && remain[i].first < dist[remain[i].second][0])
			need_cov[remain[i].second] = false; // 用最小的覆盖没有被覆盖的点
		else val1[tot1 ++] = remain[i].first;
	for(int i = h[1]; i; i = nxt[i])
		if(need_cov[e[i]]) val2[tot2 ++] = w[i];
	if(tot1 < tot2) return false;
	std::sort(val1, val1 + tot1), std::sort(val2, val2 + tot2);
	int i = 0, j = 0; // 双指针
	while(i < tot1 && j < tot2)
		if(val1[i] >= val2[j]) i ++, j ++;
		else i ++;
	return j >= tot2;
}
int main() {
	scanf("%d", &n);
	LL l = 0, r = 0; bool ok = false;
	for(int i = 1, a, b, c; i < n; i ++)
		scanf("%d%d%d", &a, &b, &c), add(a, b, c), add(b, a, c), r += c;
	scanf("%d", &m);
	for(int i = 1; i <= m; i ++) scanf("%d", armies + i);
	dep[1] = 1, dfs1(1);
	while(l < r) {
		int mid = (l + r) >> 1;
		if(check(mid)) r = mid, ok = true;
		else l = mid + 1;
	}
	printf("%lld\n", ok ? l : -1LL);
	return 0;
}
posted @ 2022-09-29 13:49  azzc  阅读(23)  评论(0编辑  收藏  举报