杂题记录2
P3515 [POI2011] Lightning Conductor
此处主要记录不用决策单调性的做法。
- 我们发现根号的取值是 \(O(\sqrt{n})\) 级别的。于是在每一个位置枚举根号取值然后在对应前后缀中查询 \(a_j\) 最值,这样算法是 \(O(n\sqrt{n})\) 的。
- 使用贡献法,对于每一个位置 \(i\) 考虑对别的位置的贡献,只需要在每一个根号值发生变化的地方打上标记即可。
- 优化:我们发现一个位置能对之后产生贡献的必要条件为这个位置的值大于之前的所有位置。这么一来在随机数据的情况下复杂度又下降为 \(O(n+\sqrt{n}\log{n})\)。
- 其实能产生贡献的位置更少,设序列最大值为 \(max\),那么只有值域落在 \([max-\sqrt{n},max]\) 之间的数才能产生贡献。而且该数必须是第一次出现。这就把能产生贡献的规模降到了 \(O(\sqrt{n})\),总复杂度为 \(O(n+\sqrt{n})\)。
[PR #1] 删数
其实数列是可以从左往右按顺序删除的,我们发现每次删除保留的是两边的数,也就是一个区间删完之后剩下的是左右端点,所以并不能通过左右各删一部分,使得左右边的某个数跨过了屏障相遇。这题还有区间平均数的不变性,所有剩下两个端点平均数为区间平均数不过好像没用。
部分分 \(a_i=i\),直接奇偶删即可。部分分,\(a_i \le 3\) 挺有意思的,其实我们发现本来是不能遇到能删就删,但是这个部分分策略就是能删就删,因为如果有连续的三个不同的数可以被操作,也就是 \(1~2~3\),我们发现左或右边的数无法被更左或更右的数删,因为它们已经是极值了。
部分分 \(n \le3000\),其实虽然 \(\sum n \le 10000\),但是单组数据 \(n\) 并不大,所有可以 \(O(n^2\log n)\)。易知区间删到最后就是两个端点,所以区间结果具有唯一性。于是设 \(dp_{l,r}\) 表示区间 \([l,r]\) 能否被操作,中间的分治点需要满足 \(a_{m}=\frac{a_l+a_r}{2}\)。观察性质,发现区间 \([l,r]\) 是单调的,二分寻找即可。
可以联想 NOIP2022 T3,正解是考虑差分,这样就是合并相同差分并乘以 \(2\)。固定右端点,发现可以满足条件的左端点级别在 \(O(\log n)\)。这里有一个小技巧,我们发现呈 \(2\) 倍关系的值 \(\frac{x}{lowbit(x)}\) 相同。
ARC067F Yakiniku Restaurants
法一:设 \(f_i\) 表示 \(i\) 的转移点,我们发现 \(f_i\) 具有单调性,分治即可。
法二:差分其实还有标记功能,对于我们用 \(s_{l,r}\) 表示 \([l,r]\) 的贡献,对于每一种烧烤券,每家烧烤店都有一个左右的控制范围,在那个范围内对于该烧烤券必然选择该店,假设为 \([l,r]\) 于是对于左端点位于 \([l,i]\) 且右端点位于 \([i,r]\) 的决策必然选择该店。于是二维差分标记一下即可。
UVA10934 装满水的气球
抽象题目,设 \(dp(i,j)\) 表示用 \(i\) 个球,实验 \(j\) 次可以确定最大高度。
本次测试的楼层应该是 \(dp(i-1,j-1)+1\),因为如果本次气球破了,保证可以用剩下的操作次数和气球完成实验。我们要求的是最高楼层所以应该是要加上最好的情况,也就是气球没破,那就还能往上扩展 \(dp(i,j-1)\) 层。于是 \(dp(i,j)=dp(i-1,j-1)+dp(i,j-1)+1\)
P7537 [COCI2016-2017#4] Rima
先考虑暴力,就是两两算一下能不能押韵,然后建边,以每个点为起点跑一遍最长链。
我们可以在字典树上解决这个问题,我们建反串,仔细思考一下这个问题,其实就是在从一个节点开始在字典树上行走,每次可以到父亲或者同父的兄弟,问最多能到达多少节点,树形 dp 一下即可。
CF1943E2 MEX Game 2
像这种求 \(mex\) 的题目一般都是枚举 \(mex\) 判断是否可行。本题可以将枚举改为二分,然后就是需要判断能否 Alice 无法取到 \([1,x]\) 内的所有数。
明确一下双方的策略, Alice 肯定是先取小的。考虑这么一种情形,就是 Bob 将全部 \(k\) 次花在了第 \(i\) 堆上但是没拿完,那么 Alice 一下将第 \(i\) 堆删掉,以此类推,这种显然 Bob 的努力也就白费了,所有正确的策略应该是均匀地取 \([suf_x,x]\) 之内的数。于是需要保证当 Alice 取到 \(suf_x\) 的时候,\([suf_x,x]\) 之间的极差小于等于 \(1\)。这个位置显然是可以二分的。找到这个位置之后如何操作呢,我们发现此后的每个位置基本上是等价的,于是只需要求出总和 \(sum\) 和剩余个数 \(res\) 就行了,然后根据 \(Alice\) 策略每次 \(sum \to sum-\lfloor\frac{sum}{res}\rfloor\),然后 \(res \to res-1\),同时 \(sum \to sum-k\)。如何快速判断 \((sum,res)\) 是否符合要求呢,有个小技巧可以预处理递推数组 \(dp_i\) 表示剩余 \(i\) 个的时候能满足的最大 \(sum\)。
CF201E Thoroughly Bureaucratic Organization
可以得出最后的排列当且仅当每个位置 \(i\) 对应的询问集合互不相同。
考虑二分答案次数 \(k\) 转化为判定,于是我们就需要找到 \(n\) 个 \(k\) 位的互不相同的二进制串,使得任意一位的 \(1\) 总数不超过 \(m\)。这里需要大胆猜想一下,只需要总数不超过 \(mk\) 就行了,至于证明就是考虑某一位 \(i\) 个数大于 \(m\) 个,那么不断移动到小于 \(m\) 的位置 \(j\) 上,那么会不会有冲突呢,也就是移过去之后发现有一个数其他位相同且正好也是 \(i\) 无,\(j\) 有。可是因为 \(i\) 的数比 \(j\) 多,所以总能找到一种搭配使得与 \(j\) 不冲突。然后只需要贪心地从 \(1\) 个 \(1\) 开始放就行了。
CF333E Summer Earnings
转化一下题意就是选择三个点,使得它们构成三边的最小值最大。
直接枚举三边是 \(O(n^3)\) 的,于是我们可以先将边降序排序,发现一条边可以产生贡献,当且仅当它所连两点在此前都和另一个点连通过了(这说明这两条边比这条边大)。这可以用 bitset 判断。
CF878D Magic Breeding
神仙题...
发现每次需要维护处理的信息量是 \(O(n)\) 的,难以通过数据结构之类的操作。
同时注意到每一种特征是独立的,而该特征的取值只有不超过 \(12\) 种,这启发我们对于取值进行操作。
首先对于每种特征的取值离散化,排名为 \(k\) 的取值的 \(1-k\) 位为 \(0\),\(k+1-12\) 位为 \(0\),于是取 \(\min\) 就变成了按位与,取 \(\max\) 变成了按位或。
此时还是有点难以维护 \(n \times k\) 列的 bitset,我们发现由于是 \(0/1\) 串,于是最多存在 \(2^k\) 种本质不同的列。
对于每个物品,我们只需要维护其 \(2^k\) 种列的变化(无论他有没有这种列)即可。
P10308 「Cfz Round 2」Osmanthus
如果打表需要打不同类型的表找规律,而不是对着某一个地方死打,这题的思路也是的,两种思路,拆位走不通就走 \(x~xor~x=0\)。后者是可以的,我们回归需要异或上同一个数抵消了,于是类似线段树结构分组,第一组搞完了,异或一遍才能消掉第二组的数,然后一二合并才能消掉后面的数。于是答案就是 \(2^{\lceil logn \rceil}\) ,注意这里的 \(n\) 是去除前导 \(0\) 后的序列长度。
「NOIP 多校联考2019」修改权值
如果往树上某点的值往什么地方修改那么想法就偏了,因为会上下影响,这种题目要有一个意识就是调整是很自由的,一旦某些东西确定了,其他地方必然有一种方式满足。
需要善于转化其实就是树上 LIS,于是我们用 set 启发式合并,每次二分更新即可。
CF1503D Flip the Cards
首先一张卡片同时包含 \(\le n\) 或者 \(\ge n\) 显然不行。
这种题目还可以随意排序,有些混乱,于是我们考虑统一形式,先不管操作次数只去满足可行性。假设现在正面朝上的都是 $ \le n$,设反面是 \(f(i)\)。于是最后肯定是前面一些 \(i\),后面一些 \(f(i)\)。
其实就等价于按 \(i\) 排序后的序列划分成两个 \(f(i)\) 单调递减的序列。
这可以通过开两个栈,每次贪心地放入栈顶较小地栈来完成。最后的答案统计就是看两个栈中翻转谁需要的代价下,就选谁。
那么如果最优化呢,如果要最优化不一定是放栈顶较小的,可能使得可行性劣一点能使答案更优。
序列问题有一种很典型的划分方式,就是根据最大最小值来划分。根据位子 \(i\) 满足 \(\min\limits_{1 \le j \le i} f(j)>\max\limits_{i<j \le n}f(i)\) 来划分,这样每一段显然是可以拼接的。注意到栈顶的两个元素 \(s_1\) 和 \(s_2\) 满足 \(\min(s1,s2)\) 为序列前缀最小值。当我们放入 \(f(i)\) 的时候,如果有\(s_1>f(i)\) 且 \(s_2>f(i)\),根据划分方式后面必然存在 \(f(j)>\min(s1,s2)\),如果用 \(f(i)\) 替换 \(\max(s1,s2)\) 必然会使得后面的 \(f_j\) 不满足要求,所以我们只能被强制要求放到较小栈了,这就唯一确定了。
CF1805F Survival of the Weakest
先是 easy 版本,队列求前 \(k\) 大是很简单的,暴力合并 \(F\) 即可。这里需要注意我们不能在中途取模,这样会改变相对大小关系,但是为了保持相对大小我们可以给序列同时减去 \(a_1\),在第 \(i\) 步我们减去的 \(a_1\),后面因为合并会被加上 \(a_1 \times 2^{n-1-i}\)。
然后是 hard 版本,打个表或者猜测一下,发现越到后面编号越大的位置能被用到的可能性越小。每次只保留前面一部分即可。
P10220 [省选联考 2024] 迷宫守卫
首先注意一个要点,就是满二叉树的树高是 \(\log n\) 级别的,这使得后续复杂度有保障。
最优化排列思路一般是逐位确定,Alice 首先应该最优化第一位,同时最小化代价,可是我们似乎不太方便直接找到最大的满足条件的第一位。那么不妨二分下 \(x\),我们需要封堵 \(< x\) 的所有位置,直接 dp 一下最小代价看看是否当前能承受即可。
注意一下,因为我们要求 Alice 要提前设置好所以为了封堵 \(x\) 左右边都需要设置,所以这里是 \(+\) 而不是 \(\max\)。同时将题目中的提前设置转化为这里的动态设置也值得思考。
确定了第一位 \(P_1\) 之后,Bob 必然是直接奔向 \(P1\) 所在叶子节点,然后退出来进行其他访问。由于是要按照类似 dfs 顺序访问,因此 Bob 活动在访问完第一个子树后似乎受到了限制,没法全局跑来跑去了,但是我们仍然可以对于 Bob 下一次进入的子树进行二分 \(+\) dp,这样就完美解决了。
还有几个细节,算出左右边的代价之后我们不要着急布置,因为目前的费用肯定是可以支付左右代价的,我们等进入对应位置之后再布置。同时我们发现使用 \(w\) 是有点亏的行为,所以计算的时候虽然是取 \(\min(w_u,f_{rc})\),但是实际我们发现如果剩余费用可以支付 \(f_{rc}\),那么就不用 \(w_u\)了,因为 \(f_{rc}\) 这么多费用在进入子树内是必然要被用掉的。
ABC221G Jumping sequence
发现每一步都选择一个方向都太难搞了,考虑 \((x,y) \to (x+y,x-y)\) 旋转一下坐标系,那么每一个方向就是独立了的,我们每次可以在每个方向上选择前进还是后退。于是对于每个维度都有
选择 \(p_i \in \{-1,1\}\),使得 \(\sum p_i\times d_i=T\),其中 \(T=A+B,A-B\)。
这很像背包的形式,两边同时加上 \(\sum d_i\),再除以 \(2\),得
选择 \(p_i \in \{0,1\}\),使得 \(\sum p_i \times d_i=\frac{T+\sum d_i}{2}\)。
这是一个 0/1 背包的形式,可以用 bitset 辅助完成。
AGC018C Coins
三元选择显然是不太方便,我们可以先假设全选金币,然后再从金币中选一部分改为银币,还有一部分改为铜币,这是一个二元选择的问题。
我们可以用交换法来确定选择策略,设 \(s_i=b_i-a_i\),\(t_i=c_i-a_i\)。对于 \((s_i,t_i)\) 与 \((s_j,t_j)\),目前 \(i\) 选 \(s\),\(j\) 选 \(t\),考虑何时交换更优。
满足 \(t_i-s_i+s_j-t_j \ge 0\),即 \(s_j-t_j \ge s_i-t_i\) 的时候 \(j\) 选 \(s\),\(i\) 选 \(t\) 更优。于是我们按照 \(s_i-t_i\) 升序排序。所有选 \(s\) 的都在选 \(t\) 的左边。但是注意这里不可选择前 \(y\) 个用 \(s\),后 \(z\) 个用 \(t\),而是应该找到一个分界点,满足在分界点之前中分配 \(s\),分界点之后分配 \(t\)。于是预处理一下前缀前 \(y\) 大 \(s\) 和后缀前 \(z\) 大 \(t\),然后枚举分界点即可。
CF407D Largest Submatrix 3
很显然的暴力枚举左右边界,对于上下进行双指针,时间复杂度 \(O(n^4)\)。
思考一下复杂度花在哪里了,主要是扫描线每次推进的代价太大了,由于左右列间距很大,每次推动指针向下暴力扫描那一行的信息很浪费时间。考虑移动枚举左右边界的时候一点点移动继承上一次的一点信息。
仔细想想好像有点困难,虽然单纯继承前几列的信息很容易,但是加入新的一列面临的是很长一段新的信息如何整合?我们可以同时在每次下移下边界的时候,只计算新移动出来的这一行,然后对于上一行算出来的上边界结果取一下交即可。
总结一下就是我们维护 \(f_{h,l,r}\) 表示只考虑第 \(h\) 行,列区间在 \([l,r]\) 之内的最多能延生的上边界(上为开区间),行内更新 \(\max(f_{h,l+1,r},f_{h,l,r-1}) \to f_{h,l,r}\),同时我们可以发现对于 \(a_{h,l}\) 没有统计其在 \(r\) 列的情况(\(a_{h,r}\) 同理),于是我们维护 \(lst_{t,j}\) 表示对于数字 \(t\) 在第 \(j\) 列上一次出现的位置,然后 \(\max(lst_{a(h,l),r},lst_{a(h,r),l}) \to f_{h,l,r}\)。最后第 \(h\) 行作为下边界能取到最远上边界就是 \(\max(f_{h,l,r},\max\limits_{c=1}^{h-1}(f_{c,l,r}))\)。
CF571D Campus
考虑只有第一类集合怎么做,
P6163 [Cnoi2020] 领域极限
常见套路拆绝对值,然后算贡献不妨设 \(a_i\) 升序。
于是