首页 > 其他分享 >Luogu-P4654-[CEOI2017] Mousetrap

Luogu-P4654-[CEOI2017] Mousetrap

时间:2023-12-07 21:13:10浏览次数:33  
标签:P4654 老鼠 操作数 int Luogu father 节点 Mousetrap 分支

前言

模拟赛之后被胁迫上去讲这题,没怎么准备,然后就在几个省的 OIer 面前当小丑。。倒是把我自己讲得很明白,但感觉对其他人不是很负责任,就来赎罪一下。。

更好的阅读体验

题意

题目链接

分析

  1. 以 \(t\) 为根,我们的目的是让老鼠走到根的操作数最小。

  2. 观察老鼠的动向,显然老鼠只要一往下走,那么除非管理员将它上方的道路擦干净,否则它就只能继续往下走。当然,老鼠可能也会往上走(待会儿再考虑)。

  3. 若老鼠已经往下走,但未走到叶子节点,那么我们不用立刻将其往上的一条路的分支堵住,因为当老鼠自己到叶子节点之后就动不了了,这时我们再把从这个叶子节点往上直到根的路径上所有的分支(不包括根的分支)全部堵上,最后再一一擦干净老鼠的边,使其被迫走向根节点。如果不堵分支,那么老鼠走入分支消耗的操作数一定大于等于 \(1\) 即直接堵的操作数。

  4. 因为是最优策略,所以每个点的最优操作数之类的信息唯一。

  5. 根据 4 提供的思路,考虑 \(f_u\) 表示老鼠从 \(u\) 点出发往下折腾一通再被迫回到 \(u\) 点在 \(u\) 子树内消耗的操作数。考虑 DP,由于每次老鼠出发前管理员可以先进行一次操作。只观察 \(u\) 点的儿子节点,发现如果不把向最大值 \((f_v)_{\max}\) 的路径堵住的话,老鼠一定选这条路往下走,折腾 \((f_v)_{\max}\) 次操作后,回到 \(u\) 点;否则老鼠会向次大值 \((f_v)_{\max_2}\) 走,折腾 \((f_v)_{\max_2}\) 次操作后回到 \(u\) 点。我们发现两种情况老鼠回到 \(u\) 点后情况其他都一样(分支全部被堵住,只能往上走),所以选择堵 \((f_v)_{\max}\) 一定最优(我们还可能堵与它不相连的边,但贡献一定小于等于堵 \((f_v)_{\max}\) 的贡献,显然)。

  6. 考虑如何求 \(f_u\),发现实际上根据 3 可知管理员的固定策略,\(f_u\) 分为三部分:一、选择最大的 \(f_v\) 堵住,使从 \(u\) 点只能走向次大 \(f_v\);二、在老鼠抵达叶子后,利用多出的时间堵上 \(u\) 的其他分支;三、把老鼠往下走过的路径擦干净,让老鼠走回 \(u\) 点。于是有转移方程:

    \[f_u=(f_v)_{\max_2}+deg_u-1 \]

    其中 \(deg_u-1\) 表示 \(u\) 点的分支(度数)去掉连向 \(father\) 节点的,去掉 \(u \to v\) 走向次大子节点的(这个不堵),加上擦老鼠 \(v \to u\) 走上来的边。再加上原本的次大子节点,就是 \(f_u\)。

  7. 考虑由 \(f_u\) 求出具体的操作次数。记录 \(g_u\) 表示 \(u\) 到 \(t\) 路径上管理员需要堵的分支(不包括 \(u\) 本身的分支),则可知 \(g\) 的转移方程:

    \[g_u=g_{father}+\max(deg_{father} - 2, 0) \]

    解释一下,\(u\) 向上的需要堵的分支,等于其 \(father\) 节点向上需要堵的分支加上 \(father\) 节点自己的分支(度数,去掉连向 \(father\) 的父节点的边,去掉连向 \(u\) 的边)(特殊情况用 \(\max\) 判断)(特别地,钦定 \(deg_t=1\)(实际上,从本题的数据上看,不影响))。

    然后我们记录一个 \(cnt_u\) 表示 从 \(u\) 的父节点走入 \(u\) 后 并最终结束游戏所需要的操作数。显然 \(cnt_u\) 存在唯一值。

    知 \(cnt_u\) 的转移方程为:

    \[cnt_u=f_u+g_u+1-(father\neq m) \]

    再解释一下,\(f_u\) 是老鼠往下走最终回到 \(u\) 点时 \(u\) 子树内的操作数贡献,\(g_u\) 是管理员不得不堵的 \(u\) 到 \(t\) 路径上的分支,额外加的 \(1\) 是擦干净 \(father \to u\) 这条边的操作。最后的 \(01\) 判断意思是:若父节点不是 \(t\),则老鼠自己是从 \(father\) 的另一个分支走入 \(father\) 再走下 \(u\) 的,老鼠已经自己弄脏了一条边,管理员就可以少堵一条。

  8. 然后我们会发现我们记录的值似乎没什么卵用。

  9. 但是先别重开。再次分析老鼠的固定策略。显然老鼠可以向上走,进入一条比直接从 \(u\) 往下走更优的分支,最大化答案。

  10. 考虑简化问题。考虑二分。对于一个待检查的最大操作数 \(T\),显然此时问题变为了:老鼠能不能使最大操作数大于 \(T\)

  11. 考虑简化老鼠的策略,并分析整个游戏的固定流程分层。老鼠显然可以先往上走 \(k\) 次(\(k \geq 0\))。然后选择当前点的一个分支,从当前点走入这个分支,(根据 4 知)老鼠走到叶子节点,管理员堵分支,老鼠出来走入根节点 \(t\),游戏结束。

  12. 考虑 11 中“从当前点走入这个分支”的隐含内容,显然走入分支后,剩下的操作数由 \(cnt\) 可以直接计算。

  13. 考虑二分检验时模拟过程。记 \(\mathrm{check}(u, p, q)\) 表示当前在 \(u\) 点,最多还可以进行 \(p\) 次操作,管理员提前保留了 \(q\) 次操作,能否满足。这里需要说一下,因为两方都执行最优,所以管理员、老鼠互相可以知道对方的一定的策略。所以管理员会在每次可操作时堵上老鼠走入会使总操作次数大于 \(p\) 的分支。因为管理员执行最优,且方案一定,所以管理员可以在不需要堵当前点的分支的时候提前堵后面的点,形象地说,管理员将操作数保留累积下来,在以后的点中使用。

  14. 考虑如何实现 \(\mathrm{check}(u, p, q)\)。记 \(v\) 为 \(u\) 的任意儿子(当然,要求 \(u\) 不是从 \(v\) 走上来的)。首先为了使老鼠在当前点无法直接使操作数大于 \(p\),我们要将所有 \(cnt_v>p\) 的分支堵住。令这些分支的数目为 \(x\),显然若 \(x>p\) 或 \(x>q\) 就返回 \(\text{false}\)。否则保留的操作数 \(q\) 用去 \(x\),同时老鼠往上走,时间 \(+1\),所以 \(q\) 又多累积了 \(1\);同时最大的剩余操作数 \(p\) 也用去 \(x\)。

    这时显然有:

    \[\mathrm{check}(u,p,q)=\begin{cases} \mathrm{check}(father_u,p-x,q-x+1) & p \geq x , q \geq x \\ \text{false} & \textrm{otherwise} \\ \end{cases} \]

    可以递归或循环实现。

    特别地, \(\mathrm{check} (t, i, j)=\text{true}\)。

    注意记 \(u\) 是从 \(last\) 走上来的,对于 \(u\) 的儿子特殊判断 \(v \neq last\)。对于 \(u=m\),\(last=0\)。

  15. 所以每次检查答案 \(T\) 是否正确的返回值就是 \(\mathrm{check}(m,T,1)\)。

Code

#include <bits/stdc++.h>
using namespace std;
int n, t, m, a, b;
int fa[1000005], dg[1000005], f[1000005], g[1000005], cnt[1000005];
int firs[1000005], nex[2000005], to[2000005], tot;
void Add (int u, int v){
    ++ tot;
    nex[tot] = firs[u];
    firs[u] = tot;
    to[tot] = v;
}
void init (int u, int father){
    fa[u] = father;
    g[u] = g[father] + max (dg[father] - 2, 0);
    int max1 = 0, max2 = 0;
    for (int e = firs[u];e;e = nex[e]){
        int v = to[e];
        if (v == father)
            continue;
        init (v, u);
        if (max1 < f[v]){
            max2 = max1;
            max1 = f[v];
        } else
        if (max2 < f[v])
            max2 = f[v];
    }
    f[u] = max2 + dg[u] - 1;
    cnt[u] = f[u] + g[u] + 1 - (fa[u] != m);
}
bool Check (int p){
    int q = 1, las = 0;
    for (int u = m;u != t;u = fa[u]){
        int x = 0;
        for (int e = firs[u];e;e = nex[e]){
            int v = to[e];
            if (v == fa[u] || v == las)
                continue;
            if (cnt[v] > p)
                ++ x;
        }
        q -= x;
        p -= x;
        if (q < 0 || p < 0)
            return false;
        ++ q;
        las = u;
    }
    return true;
}
int main (){
    scanf ("%d%d%d", &n, &t, &m);
    for (int i = 1;i < n;++ i){
        scanf ("%d%d", &a, &b);
        Add (a, b);
        Add (b, a);
        ++ dg[a];
        ++ dg[b];
    }
    dg[t] = 1;
    init (t, 0);
    int L = 0, 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;
}

标签:P4654,老鼠,操作数,int,Luogu,father,节点,Mousetrap,分支
From: https://www.cnblogs.com/imcaigou/p/17883953.html

相关文章

  • 【luogu帖】CSP-J 2023 模拟赛 01 赛时答疑帖
    赛时禁止用户与他人交流比赛相关内容,禁止在答疑帖发其他无关内容。欢迎大家参与CSP-J2023模拟赛01。这里是本场比赛的答疑帖。我向各位参赛者及谷友们的支持表示感谢。请不要在赛前在本帖中发布过多灌水相关言论,赛时禁止在本帖中发布灌水相关言论。如果对题面有不理解建议......
  • 【luogu题解】U388218 数数
    数数题目描述给定n个不超过1.5×10⁹的自然数。求这些自然数各自出现的次数,并按照自然数从小到大的顺序输出统计结果。输入格式输入的第1行是整数n,表示自然数的个数。第2行到第n+1行每行一个自然数。输出格式输出文件包含m行(m为n个自然数中不相同数的个......
  • luogu P3783 [SDOI2017] 天才黑客
    题面传送门为啥大家都写两个log的线段树优化建边啊,神秘,这1log做法好想又好写捏。首先显然是可以把边看成点的,这样会变成\(O(m)\)个点和\(O(m^2)\)条边,寄。但是还没有完全寄掉,我们发现,对于原图的每个点,对于第一个跑到这个点的边暴力转移,剩下的边转移只有一个子树,否则会......
  • luogu2839题解
    [国家集训队]middle题目分析代码如下。#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;typedefunsignedlonglongull;constintMAXN=2e4+10;intx,n,Q,a[MAXN],q[6],root[MAXN],b[MAXN],tot;vector<int>locp[MAXN];structSegmentTreeNode{......
  • luoguP4609 [FJOI2016] 建筑师
    题意:有n个高度1-n的楼房,从右看能看到a个,从左看能看到b个,问楼房有多少种排列方式。分析:首先,高度为n的建筑是肯定不会被挡住的,可以把它作为一个分水岭,在它左边的被左边的建筑挡住,在它右边的被右边的建筑挡住。由此我们可以把所有的建筑分成a+b-1个部分,每个部分由这个部分最高的建......
  • 【luogu题解】T378828 位运算
    位运算题目背景题目由daiyulong20120222创作(me)并由QBW1117完善以及数据。题目描述给定两个数\(x,y\),在给定一个位运算符号\(c\)。请你列出\(x,y\)进行\(c\)位运算是的算数竖式式。注:竖式这么列:显示出两个数的完整二进制,包括前导零。32个'-'。显示出......
  • [Luogu] P7910 [CSP-J 2021] 插入排序
    [CSP-J2021]插入排序-洛谷昨天下午爆肝一下午都没整出来(悲是我太菜了思路第一种想法,暴力即,每次修改操作后重新维护整个数组,时间复杂度\(O(Qn^2)\),能拿52pts但是,想要拿满分,很简单,只需要把排序的双层循环\(n^2\)变为\(n\)即可因为冒泡是对每个点都进行枚举,但是需要修改的......
  • [Luogu] P7911 [CSP-J 2021] 网络连接
    [CSP-J2021]网络连接-洛谷距离CSP2023还有\(**3**\)天题意及思路恶臭大模拟,按照题意模拟即可。有几个代码上的难点:当定义了一个scanf或者sscanf并且有一定的输入规则,那么如果读取到的字符串不符合定义的规则,那读入了几个变量就返回几个变量例如,如下代码定义了一个读......
  • [Luogu] P1164 小A点菜
    题目传送门一道动态规划,\(dp_{i, j}\)表示用前\(i\)个菜品花光\(j\)元的方法总数那么可以推出状态转移方程:\(if(j>a_i)\spacedp_{i,j}=dp_{i-1,j}+dp_{i-1,j-a_{i}}\)如果j比ai大,那么方案数就是不买\(dp_{i − 1, j}\)+买\(dp_{i − 1, j − a_i}\),其中如果买,那么......
  • [Luogu] P1114 “非常男女”计划
    https://www.luogu.com.cn/problem/P1114暴力,前缀和,稍加优化可以拿100,但是#1加强过后就AC不了了#include<bits/stdc++.h>usingnamespacestd;constintmaxn=2e6;inta[maxn],n,f[maxn],ans,boy;intmain(){ cin>>n; for(inti=1;i<=n;i++) { scanf("%d",a......