2024.1 做题记录
P2423 [HEOI2012] 朋友圈
考虑 \(a \oplus b \bmod 2 = 1\) 的限制实际上转化为不同左侧点最多选择两个,因为奇偶性需要不同。
暴力枚举左侧的点集,考虑 B 侧的点,首先需要跟左侧点集任意有边,之后内部还需要是完全图。
B 侧选定点的最大团这个东西是不好做的,但是我们可以借助边的性质。
我们如果按照 B 的奇偶性来分成两类,因为 \(a \oplus b \bmod 2 = 0\),所以集合内部点两两有边,而第二类又在两个集合中做出了连边。
不难发现如果除去团内的边,实际上这是一张二分图,我们将二分图取补图,这仍然是一张二分图。我们发现,如果在这张图上求 最大独立集,那么在原图上即为最大团。
团要求两两有边,而独立集要求两两没边,将补图还原后两两间必然有边,即为团。
- 引理:最大独立集 = $n \ - $ 最小点覆盖。
最大独立集事实上想要从全集中去掉一些点(尽量少),使得两两间没边,也就是想找最少的点破坏掉所有边(如果有边存在必然有点相连)。正好对应了最小点覆盖。
而最小点覆盖又等于最大匹配。所以我们对补图求最大匹配然后计算即可。
时间复杂度 \(O(|A|^2|B|^2)\)。
BZOJ #4664
考虑方案数从前到后并不好表示,因为如果想保证混乱度 \(\le L\),那么需要记录前一项是什么,还需要记录选了什么,这样的话状态数也许是 \(O(2^nn)\) 的,显然无法接受。
考虑一个插块 dp,设 \(f_{i, j}\) 表示从小到大插入到第 \(i\) 个数,分成了 \(j\) 段的方案数。
转移经典,但是我们发现无法保证混乱度,考虑再设 \(f_{i, j, k}\) 表示从小到大插入到第 \(i\) 个数,分成了 \(j\) 段,总混乱度为 \(k\) 的方案数。
看起来到这就结束了,不过我们发现我们很难考虑一次插值的贡献。事实上,贡献是与邻项强相关的,然而我们的状态并没有记录邻项的值。
考虑一边插值一边计算全局的贡献。具体来说,从小到大给了我们很好的性质,考虑这样一个式子:
事实上这就是差分的前缀和。因为我们从小到大插值,那么未插的地方数字一定比当前值大,为了构造出它,我们给所有连通块的边界位置插上 \(a_i - a_{i - 1}\) 的贡献,一边插值一边算出了整体的贡献。
实现细节上,因为边界只有一边可以产生贡献所以需要再开一维表示边界。
accnoi5709 k 大值
事实上也许跟基数排序没什么关系。
考虑值域大概是 \(10^{18}\),空间正好容得下 \(10^6\),按照 \(10^{12}\) 确定答案在哪个块里,再按照 \(10^6\) 分块,确定在哪个 \(10^6\) 块里,下一次内部开桶即可。
ps: 事实上值域不是 \(10^{18}\),而是稍大一些,实现上可以适当调大块长。
P1640 [SCOI2010] 连续攻击游戏
纯题。原本以为是 2-sat 类状物。
将每个阶段设出点,然后每个武器练两条边,只能用一次正好满足匹配的意思,直接匈牙利即可。
CF86D Powerful array
纯莫队。据 gqh 说是防止老年痴呆的,挺有道理。
CODECHEF Children Trips
这个东西首先转化为分别从 \(s, t\) 开始分别往上跳的相遇步数。
考虑对步长根号分治一下。对于 $p \ge \sqrt{n} $,我们最多只需要走 \(\sqrt{n}\) 步就能相遇,这一部分如果用倍增维护出往上走 \(p\) 的点是什么可以做到 \(O(\log p\sqrt{n})\) 的复杂度。
然后考虑 \(p < \sqrt{n}\) 的部分。这一部分的值我们发现不多,可以对每一种步长重构倍增树,具体的,我们将原树用倍增缩为一棵走一步相当于是一天的树,再对这棵树做一次倍增预处理,这样我们暴力跳,复杂度是 \(O(n\log p + n\sqrt{n}\log n + \sqrt{n}\log n) = O(n\sqrt{n}\log n)\),所以总复杂度就是 \(O(n\sqrt{n}\log n)\)。
[ABC154F] Many Many Paths
首先把典的方案数转化为 \(\displaystyle\binom{i + j}{i}\),然后考虑公式
然后把每一行转化为两个前缀和,直接带入上面的每次就能 \(O(1)\) 了,复杂度 \(O(n)\)。
[十二省联考 2019] 异或粽子
套路的转化为若干个前缀和异或的形式。然后考虑合法区间呈三角,这样的东西可以可持久化一下,但事实上如果直接补齐三角然后将 \(2k \to k\),最后再减半答案,效果是一样的。
显然每个最优解会被取到两次,答案取半就是对的。
由于 \(k\) 不大,考虑实际的算出前 \(k\) 个都是什么。注意到,如果我们把所有的异或对算出来然后排序取前 \(k\) 大就是答案。
事实上当 \(k \le 1e4\) 都没什么问题。不过注意到我们有很多无用答案,那么如果我们只记录对于每个下标的最大的 没被使用过的 区间,然后贪心的扔进堆里每次取 \(\max\),然后再将同一位置但是是第 \(k + 1\) 大的扔进去接着贪, 答案就是对的。
这样的复杂度是 \(O(k\log n)\)。
回文串
很显然的是如果 \(S_1 \neq S_{|S|}\),那么反转区间一定包含端点之一,我们固定端点直接哈希即可。
对于 \(S_1 = S_{|S|}\) 的,我们直接截掉两端相同的部分即可。转化为两端不同的情况。
CF1906B Button Pressing
先考虑转化到前缀和上,然后考虑操作意义。
- \(i \neq 1\)
因为需要保证 \(a_i = 1\),所以 \(s_i \neq s_{i - 1}\),操作相当于让 \(s_i\) 和 \(s_{i - 1}\) 翻转,并且因为原来不同所以操作可以视作交换了 \(s_i\) 和 \(s_{i - 1}\)。所以如果 \(1\) 个数相等可以通过冒泡得到 \(b\)。
- \(i = 1\)
考虑对 \(i = 1\) 的操作,相当于对 \(i \in [2, n]\) 的 \(s_i\) 进行翻转,设原 \(1\) 的数量为 \(t\),翻转后 \(n - t + 1 \to t\),此时数量相等也可以。
如果 \(a_1 = 0\),可以考虑把 \(1\) 交换到 \(a_1\) 进行操作。而如果全 \(0\) 则会在第一种情况内被判掉。
[NOI2005]瑰丽华尔兹
朴素 dp 很好想,直接有 \(f_{t, x, y}\) 关于时间坐标的暴力 dp,复杂度 \(O(Tnm)\),考虑优化。
考虑实际上我们对于一个时间段内的转移是连续的(对于 \((x, y)\)),并且并不需要真的记下来具体时刻,如果不可达直接刷掉即可。那么一次就可以做出一行或者一列的 dp 更新。
考虑填表的东西,实际上是 \(p \in [i - len, i]\) 内的 \(f_p + i - p\),能填的东西是一段窗口,直接单调队列即可。
对于障碍直接清空队列。
洪水
暴力 dp 考虑 \(f_u\) 表示使得 \(u\) 子树内叶子都不可达的最小代价。有转移
其中 \(son_u\) 表示 \(u\) 的儿子。
考虑带修改怎么关联到常用方法 ddp 上。先树剖,令 \(s_u\) 表示 \(u\) 的重儿子,然后把贡献拆成
其中 \(g_u = \displaystyle\sum_{v \in son_u, v \neq s_u}f_v\)。(事实上这样做的主要目的就是为了方便放到矩阵上。)
然后我们定义广义矩阵乘法是
这么定义的目的,是为了让 \(f\) 能从式子里的 \(a_u + 0\) 和 \(g_u + f_{s_u}\) 转移过来。
考虑答案矩阵一定要含有 \(f_u\),并且转移矩阵还要有 \(a_u, g_u, f_{s_u}\),不妨直接往上填然后补掉剩下的部分。
不难得到最终由
直接按照 ddp 的方式跳重链就行了。
另外,因为我们的叶子节点上挂着的是 \(1 \times 2\) 的初始矩阵,所以肯定要算出 \(f\) 然后填在矩阵 \((0, 0)\) 处,这样出来的东西才不会是一堆转移矩阵出来的东西。
Hard Nim
考虑如果直接有 \(f_{i, u}\) 表示 \(i\) 堆式子局面为 \(u\) 的方案数的话,转移显然是枚举子集然后答案即为 \(f_{n, 0}\)。复杂度 \(O(n3^{\log m})\)。
考虑优化枚举子集的过程,事实上相当于一个异或卷积(枚举两个子集然后方案数相乘),直接 FWT 即可。复杂度 \(O(nm\log m)\)。
考虑卷出来了什么东西,我们可以凑出的方案从 \(1\) 到 \(2\) 到 \(4\) 然后每次倍增(每次选最多石子的方案),所以满足结合律,直接快速幂。复杂度 \(O(m\log m\log n)\)。
[COCI2022-2023#4] Vrsta
其实就是动态中位数,先要离散化不然维护不了。线段树二分显然可以直接做,但是不好写。
考虑类似第 \(k\) 大的 \(\log^2\) 做法,权值树状数组维护前 \(i\) 次操作的人数情况,然后二分中位数 \(mid\) 并查询 \(\leq mid\) 的人数。
瓶颈在树状数组和二分,复杂度 \(O(n\log^2n)\)。
[SCOI2015] 小凸玩矩阵
先令 \(n - k + 1 \to k\),变成第 \(k\) 小比较舒服。
每行每列只能选一个其实也就是匹配的限制,直接从任意行向任意列连边即可。
然后考虑二分每次求是否存在匹配数 \(\geq k\)(只保留所有 \(a_{i, j} \le mid\))即可。
AT_soundhound2018_summer_final_e Hash Swapping
给线段树上每个节点赋一个编号,每次交换操作相当于交换编号。(交换其父亲对应儿子的编号)
查询操作直接类似哈希的合并简单完成。
[WC2011] 最大XOR和路径
考虑答案的构成一定类似 \(1 \to n\) 的路径和若干个环的异或。
对于环,我们一来一回刚好把起点到环的贡献消掉。
考虑 \(1 \to n\) 的路径不止存在一条。但如果超过 \(1\) 条就是一个大环,我们异或上整个环就可以得到另外一条路径。
把若干个环的异或放到线性基里和 \(dis[n]\) 做最大异或即可。
P3292 [SCOI2016] 幸运数字
考虑线性基的合并是可重复贡献类的(一样的值插进去肯定不能填到新的位置)。于是可以考虑维护倍增 \(g_{u, i}\) 表示 \(u\) 点向上 \(2^i\) 维护的线性基,转移类似求 LCA,复杂度 \(O(n\log ^2V + q\log n\log^2V)\),有点忍不了。
考虑用 \(k\) 级祖先把整个路径割成两半合并,这样的复杂度就是 \(O(n\log ^2V + q\log^2V)\) 的了。
事实上,可以这么做的原因是因为线性基的可重复合并,所以才可以用类似 st 表的方式变成两段直接合并。