标签:表示 mit 距离 poi 不可 ++i ++ ring namespace
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;
}
标签:表示 mit 距离 poi 不可 ++i ++ ring namespace
原文地址:https://www.cnblogs.com/morslin/p/11855697.html