码迷,mamicode.com
首页 > 其他好文 > 详细

POI2011 DYN-Dynamite

时间:2019-11-14 11:21:54      阅读:76      评论:0      收藏:0      [点我收藏+]

标签:表示   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;
}

POI2011 DYN-Dynamite

标签:表示   mit   距离   poi   不可   ++i   ++   ring   namespace   

原文地址:https://www.cnblogs.com/morslin/p/11855697.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!