POI2011 DYN-Dynamite

题目传送门

DP是不可能DP的,只会搜索


先二分距离,然后此题转化为最小点覆盖,即选择最少的点去覆盖关键节点,覆盖范围为二分的\(mid\)
对于覆盖,大部分题解都是用的DP,但是用剪枝后的搜索也能水过去

对于每个关键节点,如果它已经被覆盖了,就不管它,否则选择它的\(k\)级祖先进行覆盖
在用DFS进行覆盖的时候,可能会有一个点\(x\)被覆盖多次,但只有离\(x\)最近的一个覆盖点才是最有用的
所以可以用一个vis[x]表示覆盖点覆盖到到\(x\),还剩下多少距离可以继续覆盖,只有当前DFS剩下的距离大于vis[x]时才继续搜索\(x\)

虽然这种优化可以AC,但应该还是可以被卡掉的,毕竟时间复杂度最坏是\(O(n^2)\)。写这篇题解的目的只是提醒一下,搜索+剪枝往往会有奇效

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
LL read() {
	LL k = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9')
		k = k * 10 + c - 48, c = getchar();
	return k * f;
}
struct zzz {
	int t, nex;
}e[300010 << 1]; int head[300010], tot;
void add(int x, int y) {
	e[++tot].t = y;
	e[tot].nex = head[x];
	head[x] = tot;
}
bool val[300010]; int f[300010];
int q[300010], h = 1, t = 0;
int vis[300010];
void bfs(int s) { //预处理
	q[++t] = s; vis[s] = 1;
	while(h <= t) {
		int x = q[h]; ++h;
		for(int i = head[x]; i; i = e[i].nex) {
			if(vis[e[i].t]) continue;
			q[++t] = e[i].t; f[e[i].t] = x;
			vis[e[i].t] = 1;
		}
	}
}
void dfs(int pos, int fa, int k) { //覆盖
	vis[pos] = k; if(!k) return ;
	for(int i = head[pos]; i; i = e[i].nex) {
		if(vis[e[i].t] < k-1) dfs(e[i].t, pos, k-1);
	}
}
void update(int pos, int k) { //找k级祖先
	int x = pos;
	for(int i = 1; i <= k && f[x]; ++i) x = f[x];
	dfs(x, x, k);
}
int n, m;
bool judge(int k) {
	memset(vis, -1, sizeof(vis));
	int tot = 0;
	for(int i = n; i >= 1; --i) {
		if(vis[q[i]] == -1 && val[q[i]]) {
			update(q[i], k), ++tot;
			if(tot > m) return 0;
		}
	}
	return 1;
}
int main() {
	n = read(), m = read();
	for(int i = 1; i <= n; ++i) val[i] = read();
	for(int i = 1; i <= n-1; ++i) {
		int x = read(), y = read();
		add(x, y); add(y, x);
	}
	bfs(1);
	int l = 0, r = n;
	while(l < r) {
		int mid = (l + r) >> 1;
		if(judge(mid)) r = mid;
		else l = mid+1;
	}
	cout << l;
	return 0;
}
posted @ 2019-11-14 11:12  MorsLin  阅读(99)  评论(0编辑  收藏  举报