• [NOIp2012]疫情控制


    Description

    Luogu1084
    给定一棵树和一些军队,军队可以移动,一个军队可以覆盖他的整个子树,求为了使所有叶子节点都被覆盖,移动时间最长的军队最短的移动时间是多少。

    Solution

    这个题不难想到二分答案,但是二分后如何验证成了一个问题。
    首先观察到一个重要的结论:在不超过根节点的前提下,一个军队的深度越浅越好,且没有变深的必要。所以,可以先上提所有军队,直至到达根节点的儿子或者到达上限。
    此时一定会有若干个军队尚可移动,将其存入数组(A)中。
    对于根的每个儿子,若其子树中的叶子节点全被无法移动的军队覆盖,那么这个儿子不会因给出军队而需要军队,所以就不管了。其他的儿子都放入数组(B)中。
    我们先排序(A)(B)。贪心的处理(B)中的每个元素(b):如果(b)这棵子树中还有可以移动的军队,那就用剩余时间最短的那个,否则就使用其它子树中剩余时间最长的那个。这样做是一定最优的,可以用交换法证明。

    Code

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    typedef long long LL;
    const int N = 5e4 + 10;
    const int M = 2 * N + 10;
    
    int hd[N], to[M], nxt[M], w[M], cnt; // 边表 
    int n, m, acnt, bcnt; // 点数,边数,A,B数组的大小 
    int army[N], fa[N][20], vis[N], used[N];
    // army 每个军队的位置 fa 每个节点的祖先(倍增)
    // vis 某个节点是否有无法移动的军队
    // used 某个军队是否被用过 
    LL dis[N][20]; // 到祖先的距离 
    struct node {
        int id; LL rest;
    } a[N], b[N], c[N];
    // a A b B c 根的每个儿子节点中可以移动的剩余时间最短的军队 
    
    inline void adde(int x, int y, int z) {
        cnt++;
        to[cnt] = y; w[cnt] = z; nxt[cnt] = hd[x];
        hd[x] = cnt;
    }
    
    void dfs(int x, int f) { // 处理倍增数组 
        for (int i = 1; i < 20; ++i) {
            fa[x][i] = fa[fa[x][i-1]][i-1];
            dis[x][i] = dis[fa[x][i-1]][i-1] + dis[x][i-1];
        }
        for (int i = hd[x]; i; i = nxt[i]) if (to[i] != f) {
            fa[to[i]][0] = x;
            dis[to[i]][0] = w[i];
            dfs(to[i], x);
        }
    }
    
    bool cmp(node x, node y) {
        return x.rest > y.rest;
    }
    
    bool check_node(int x, int f) { // 检验这个节点的子树是否不需要军队
        int is_leave = 1;
        int not_child_need_army = 1;
        if (vis[x]) return 1;
        for (int i = hd[x]; i; i = nxt[i]) if (to[i] != f) {
            is_leave = 0;
            if (!check_node(to[i], x)) {
                not_child_need_army = 0;
                if (x == 1) { // 如果是根的儿子就放入B数组 
                    b[++bcnt].id = to[i];
                    b[bcnt].rest = w[i];
                } else return 0;
            }
        }
        if (is_leave) return 0; // 如果访问到叶子,说明其没有被覆盖 
        else return not_child_need_army;
    }
    
    bool check(LL lim) {
    	// 初始化 
        acnt = bcnt = 0;
        memset(c, 0, sizeof c);
        memset(vis, 0, sizeof vis);
        memset(used, 0, sizeof used);
        used[0] = 1;
        // 移动军队 
        for (int i = 1; i <= m; ++i) {
            int x = army[i];
            int num = 0;
            for (int j = 19; j >= 0; --j) {
                if (num + dis[x][j] <= lim && fa[x][j] > 1) {
                    num += dis[x][j];
                    x = fa[x][j];
                }
            }
            if (fa[x][0] == 1 && num + dis[x][0] <= lim) { // 还能走 
                a[++acnt].rest = lim - dis[x][0] - num; a[acnt].id = i;
                if (!c[x].id || c[x].rest > a[acnt].rest) {
                    c[x].id = a[acnt].id;
                    c[x].rest = a[acnt].rest;
                }
            } else { // 不能走 
                vis[x] = 1;
            }
        }
         
        if (check_node(1, 0)) return 1;
        // 贪心的处理B中的每个元素。
    	int now = 1;
        std::sort(a+1, a+acnt+1, cmp); std::sort(b+1, b+bcnt+1, cmp);
        for (int i = 1; i <= bcnt; ++i) {
            if (!used[c[b[i].id].id]) {
                used[c[b[i].id].id] = 1;
                continue;
            }
            while (now <= acnt && (used[a[now].id] || a[now].rest < b[i].rest)) now++;
            if (now > acnt) return 0;
            used[a[now].id] = 1;
        }
        return 1;
    }
    
    int main() {
        scanf("%d", &n);
        LL L = 0, R = 0;
        for (int i = 1, x, y, z; i < n; ++i) {
            scanf("%d%d%d", &x, &y, &z);
            adde(x, y, z);
            adde(y, x, z);
            R += z;
        }
        scanf("%d", &m);
        for (int i = 1; i <= m; ++i) {
            scanf("%d", &army[i]);
        }
        dfs(1, 0);
        LL ans = -1;
        while (L < R) {
            LL mid = (L + R) >> 1;
            if (check(mid)) {
                R = mid;
                ans = mid;
            } else {
                L = mid+1;
            }
        }
        printf("%lld
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    PHP $_SERVER变量
    Buddy system伙伴分配器实现
    Linux iconv使用
    内存管理(memory allocation内存分配)
    内存碎片
    《STL源码剖析》chapter2空间配置器allocator
    Effective C++学习笔记:初始化列表中成员列出的顺序和它们在类中声明的顺序相同
    c++ explicit
    《STL源码剖析》环境配置
    C++ STL的各种实现版本
  • 原文地址:https://www.cnblogs.com/wyxwyx/p/noip201223.html
Copyright © 2020-2023  润新知