[POI2011][树上贪心] DYN-Dynamite

题面

一道比较巧妙的树上贪心题,做法同Luogu P3942 将军令
[HNOI2003]消防局的设立类似。

首先分析题面不难想到二分答案,然后我们维护两个量:
1. 当前子树下最远的需要覆盖(有炸弹)的点,记作 \(disCo_x\)
2. 当前子树外最近的被选择引燃的点,记作 \(disSel_x\)

将所有点分为三种情况考虑:
1. 自身就是等待覆盖的关键节点(炸弹),即 \(explo_x = True\)\(disSel_x \gt val\),此时 \(disCo_x\) 一定为 \(0\) (子树无炸弹)或原先值,
2. 子树中所有的炸弹被子树外的点覆盖,即 \(disSel_x + disCo_x \le val\) (或 \(disCo_x <= val - disSel_x\)),此时将子树未覆盖的点设为
不存在(\(disCo_x = -inf\))。
3. 当前点是需要选择的节点,即 \(disCo_x = val\),此时 \(cnt = cnt + 1\)
\(val\) 为二分出的最小距离最大值的最小值,\(cnt\) 为选择的节点总数。

然后跑个 \(DFS\),这题就切掉了。

代码:

# include <iostream>
# include <cstdio>
# define MAXN 300005
# define INF 1145141919.810

struct edge{
	int v, next;
}e[MAXN<<1];
int hd[MAXN], cntE;
bool explo[MAXN]; // is there a bomb or not
int disCo[MAXN], disSel[MAXN];
// dis to futhest uncovered key node, dis to closet selected key node
int cntSel, n, m;

void AddE(int u, int v);
void DFS(int now, int fa, int val);
bool Chk(int val);

int main(){
	scanf("%d%d", &n, &m);

	for(int i = 1, x; i <= n; i++){
		scanf("%d", &x);
		explo[i] = x;
	}

	for(int i = 1, u, v; i <= n-1; i++){
		scanf("%d%d", &u, &v);
		AddE(u, v); AddE(v, u);
	}

	int l = 0, r = n;

	while(l < r){
		int mid = (l + r) >> 1;
		if(Chk(mid)){
			r = mid;
		}
		else{
			l = mid+1;
		}
	}

	printf("%d", l);

	return 0;
}

bool Chk(int val){
	cntSel = 0;
	DFS(1, 0, val);

	if(disCo[1] >= 0){
		cntSel++;
	}
	return cntSel <= m; // 选择的节点不能超过 m 个
}

void DFS(int now, int fa, int val){ // val 为二分的最大值
	disCo[now] = -INF, disSel[now] = INF;
	
	for(int i = hd[now]; i; i = e[i].next){
		if(e[i].v == fa){
			continue;
		}
		DFS(e[i].v, now, val);
		disCo[now] = std::max(disCo[now], disCo[e[i].v] + 1);
		disSel[now] = std::min(disSel[now], disSel[e[i].v] + 1);
	}

	if(explo[now] && disSel[now] > val){ // 自身是需要被覆盖的节点
		disCo[now] = std::max(disCo[now], 0);
	}
	if(disCo[now] + disSel[now] <= val){ // 内部未被覆盖的点均被子树外的点覆盖
		disCo[now] = -INF;
	}
	if(disCo[now] == val){ // 当前节点恰好是需要选择的节点
		disCo[now] = -INF;
		disSel[now] = 0;
		cntSel++;
	}
}

void AddE(int u, int v){
	e[++cntE] = (edge){v, hd[u]};
	hd[u] = cntE;
}
posted @ 2020-08-24 21:13  ChPu437  阅读(117)  评论(1编辑  收藏  举报