• Solution 「CEOI 2017」「洛谷 P4654」Mousetrap


    \(\mathscr{Description}\)

      Link.

      在一个含 \(n\) 个结点的树形迷宫中,迷宫管理者菈米莉丝和一只老鼠博弈。老鼠初始时在结点 \(y\),有且仅有结点 \(x\) 布置有陷阱。一条边有切断干净三种状态,初始时所有边是干净的,每一回合中:

    • 管理者先行动:选择一条干净的边,将其切断;选择一条的边,将其清理干净;或者不进行任何操作,此时管理者所用的操作次数不变。
    • 老鼠后行动:设当前老鼠在结点 \(u\),则选择一条干净的边 \((u,v)\),走到 \(v\);当且仅当不存在这样的 \((u,v)\) 时,不进行任何操作。

    当老鼠走到 \(x\),游戏结束。管理者希望自己的操作次数,老鼠希望最大化管理者的操作次数,求双方最优决策下管理者的操作次数。

      \(n\le10^6\)

    \(\mathscr{Solution}\)

      不妨以 \(x\) 为根。若老鼠从 \(u\) 走向了 \(u\) 的某个叶子 \(v\),那么在管理者不帮忙清理 \((u,v)\) 的情况下,老鼠不能行动。这个时候管理者显然应当切断路径 \((v,x)\) 上的所有分叉边,并清理干净路径 \((v,x)\) 上的边,此后老鼠不得不走向毁灭。可见,老鼠有且仅有一次向孩子走的机会,然后就只能在子树内逃窜,最后被赶上 \(x\)

      一般的,设 \(f(u)\) 表示老鼠自觉从起点 \(y\) 走到 \(u\),且已经向孩子走过时,把老鼠赶到 \(x\) 需要的最优操作次数。不妨设老鼠从 \(y\) 向上走,在点 \(w\) 第一次向孩子走;路径 \((u,x)\) 上的分叉边数量为 \(b\);点 \(u\) 的深度为 \(d_u\)\(\operatorname{smax} S\) 表示集合 \(S\) 中的次大值,那么

    \[f(u)=\begin{cases} d_u-d_w+b&u\text{ is a leaf}\\ d_u-d_w+b+1&u\text{ has exactly one child}\\ \operatorname{smax}_{v\in\operatorname{son}(u)}\{f(v)\}&\text{otherwise} \end{cases}. \]

      此后的问题便是决定老鼠究竟在哪个 \(w\) 向孩子走。尝试二分答案为 \(x\),这个 \(x\) 需要应对两种开销:

    • 在老鼠还未向孩子走时,强制老鼠向上走;
    • 在老鼠向孩子走之后,完成计算 \(f\) 时的讨论。

    因此,模拟老鼠从 \(y\) 向上走的过程,若当前走到 \(w\)\(w\) 存在若干个在 \(x\) 步操作内无暇应付的孩子,管理者就需要把它们全部切掉,但是老鼠每走一步,管理者只能再切一条边。据此判断 \(x\) 是否合法即可。

      最终复杂度为 \(\mathcal O(n\log n)\)

    \(\mathscr{Code}\)

    /*+Rainybunny+*/
    
    #include <bits/stdc++.h>
    
    #define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
    #define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)
    
    inline char fgc() {
        static char buf[1 << 17], *p = buf, *q = buf;
        return p == q && (q = buf + fread(p = buf, 1, 1 << 17, stdin), p == q) ?
          EOF : *p++;
    }
    
    template <typename Tp = int>
    inline Tp rint() {
        Tp x = 0, s = fgc(), f = 1;
        for (; s < '0' || '9' < s; s = fgc()) f = s == '-' ? -f : f;
        for (; '0' <= s && s <= '9'; s = fgc()) x = x * 10 + (s ^ '0');
        return x * f;
    }
    
    const int MAXN = 1e6;
    int n, rt, st, fa[MAXN + 5], dep[MAXN + 5], f[MAXN + 5];
    std::vector<int> adj[MAXN + 5], brv[MAXN + 5];
    bool onp[MAXN + 5];
    
    inline void init(const int u) {
        for (int v: adj[u]) if (v != fa[u]) {
            dep[v] = dep[fa[v] = u] + 1, init(v);
        }
    }
    
    inline void calcF(const int u, int top, int brc) {
        if (onp[u]) top = u;
        brc += adj[u].size() - 1 - (adj[u].size() > 1);
        if (adj[u].size() == 1) return void(f[u] = dep[u] - dep[top] + brc);
        int mx = 0, sx = 0;
        for (int v: adj[u]) if (v != fa[u]) {
            calcF(v, top, brc);
            if (f[v] > mx) sx = mx, mx = f[v];
            else if (f[v] > sx) sx = f[v];
        }
        if (adj[u].size() == 2) f[u] = dep[u] - dep[top] + brc + 1;
        else f[u] = sx;
    }
    
    inline bool check(const int x) {
        int ban = 0, alw = 0;
        for (int u = st; u != rt; u = fa[u]) {
            for (int b: brv[u]) ban += b + ban - (u != st) > x;
            if (ban > ++alw) return false;
        }
        return true;
    }
    
    int main() {
        n = rint(), rt = rint(), st = rint();
        rep (i, 2, n) {
            int u = rint(), v = rint();
            adj[u].push_back(v), adj[v].push_back(u);
        }
        if (rt == st) return puts("0"), 0;
        
        init(rt), onp[rt] = true;
        for (int u = st; u != rt; onp[u] = true, u = fa[u]);
        for (int v: adj[rt]) if (onp[v]) calcF(v, 0, 0);
    
        for (int u = st; u != rt; u = fa[u]) {
            for (int v: adj[u]) if (!onp[v]) brv[u].push_back(f[v]);
            std::sort(brv[u].begin(), brv[u].end());
        }
    
        int l = f[st], r = n << 1;
        while (l < r) {
            int mid = l + r >> 1;
            if (check(mid)) r = mid;
            else l = mid + 1;
        }
        printf("%d\n", l);
        return 0;
    }
    
    
  • 相关阅读:
    51nod1260
    51nod1327
    51nod1342
    51nod1479
    LOJ6088
    51nod1634
    51nod1778
    JAVA循环结构学校上机经常遇到的几题 笔记
    B. The Number of Products(Codeforces Round #585 (Div. 2))
    A. Yellow Cards ( Codeforces Round #585 (Div. 2) 思维水题
  • 原文地址:https://www.cnblogs.com/rainybunny/p/15763520.html
Copyright © 2020-2023  润新知