【題解】LuoGu1084:疫情控制

原題傳送門
需要用mm個點封死一棵樹

答案滿足二分性,直接二分
本題難點在於checkcheck

貪心思想:駐紮到深度越小的點越優
通過倍增上提軍隊,有些軍隊在二分的mid範圍內走不到根節點,那麼他就駐紮在能走到的最上的地方
有些軍隊可以,那麼他們暫時在根節點休息一會兒,接着從根節點往下走到需要幫助的深度爲2的點(根據貪心思想)

通過一開始走不到根節點的點可以先處理出哪些深度爲2的點其實是不用去管的,它的葉子節點都已被軍隊保護。可以用dfs遍歷處理

接着我用空閒軍隊去幹掉那些遺留的深度爲2的點
採用兩根指針貪心的方法

Code:

#include <bits/stdc++.h>
#define maxn 50010
using namespace std;
struct Edge{
    int to, next, len;
}edge[maxn << 1];
struct node{
    int u, dis;
}a[maxn], b[maxn];
int num, num1, num2, head[maxn], d[maxn], fa[maxn][25], tag[maxn], len[maxn][25], pos[maxn], n, m;

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

void addedge(int x, int y, int z){ edge[++num] = (Edge){y, head[x], z}, head[x] = num; }
bool cmp(node x, node y){ return x.dis < y.dis; }

void dfs(int u, int pre){
    d[u] = d[pre] + 1, fa[u][0] = pre;
    for (int i = 0; fa[u][i]; ++i) fa[u][i + 1] = fa[fa[u][i]][i], len[u][i + 1] = len[u][i] + len[fa[u][i]][i];
    for (int i = head[u]; i; i = edge[i].next){
        int v = edge[i].to;
        if (v != pre) len[v][0] = edge[i].len, dfs(v, u);
    }
}

void cover(int u, int pre){
    int p = 1, q = 0;
    for (int i = head[u]; i; i = edge[i].next){
        int v = edge[i].to;
        if (v != pre) cover(v, u), p &= tag[v], q = 1;
    }
    tag[u] |= ((u != 1) & p & q);
}

bool check(int mid){
    memset(tag, 0, sizeof(tag));
    num1 = num2 = 0;
    for (int i = 1; i <= m; ++i){
        int u = pos[i], sum = mid;
        for (int j = 20; j >= 0; --j)
            if (fa[u][j] && sum >= len[u][j]) sum -= len[u][j], u = fa[u][j];
        if (u != 1) tag[u] = 1; else{
            u = pos[i];
            for (int j = 20; j >= 0; --j) if (fa[u][j] > 1) u = fa[u][j];
            a[++num1] = (node){u, sum};
        }
    }
    cover(1, 0);
    for (int i = head[1]; i; i = edge[i].next){
        int v = edge[i].to;
        if (!tag[v]) b[++num2] = (node){v, edge[i].len};
    }
    sort(a + 1, a + 1 + num1, cmp);
    sort(b + 1, b + 1 + num2, cmp);
    int j = 1;
    for (int i = 1; i <= num1; ++i){
        if (!tag[a[i].u]) tag[a[i].u] = 1; else
        if (a[i].dis >= b[j].dis) tag[b[j].u] = 1;
        while (tag[b[j].u]) ++j;
    }
    return j > num2;
}

int main(){
    n = read();
    int l = 0, r = 0, ans = -1;
    for (int i = 1; i < n; ++i){
        int x = read(), y = read(), z = read();
        addedge(x, y, z), addedge(y, x, z);
        r += z;
    }
    dfs(1, 0);
    int cnt2 = 0;
    for (int i = 1; i <= n; ++i) cnt2 += d[i] == 2;
    m = read();
    if (m < cnt2) return puts("-1"), 0;
    for (int i = 1; i <= m; ++i) pos[i] = read();
    while (l <= r){
        int mid = (l + r) >> 1;
        if (check(mid)) ans = mid, r = mid - 1; else l = mid + 1;
    }
    printf("%d\n", ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章