Hey Gift:等下一个天亮
QOJ 4217. Graph Coloring
我草这太离谱了。
考虑不要边染色,我们给每个点一个集合 \(S_u\) 表示这个点的出边有哪些颜色。考虑一条边 \(u\to v\),如果 \(u\) 的集合 \(S_u\) 被 \(v\) 的集合 \(S_v\) 完全包含,那么显然 \(u\to v\) 将不存在任何染色方法。
考虑给所有点一个集合,那么两个点 \((u,v)\) 之间的边允许使用任何 \(S_u-S_v\) 的颜色。所以我们只需构造使得 \(S_u-S_v\ne \empty\) 就好了。注意到 \({14\choose 7}\ge n\) ,我们爆搜所有大小为 \(7\) 的集合显然满足条件。
Code Festival 2016 Grand Final J. 123 Pairs
我感觉不太难,但是没有想得很远。还算有点意思的数数题,至少不是大力 dp 了。
容易想到考虑这个东西的独立性,我们把它划分为若干种独立的模块进行拼接。
首先第一个简单的想法是用第一类数对作为标识物进行划分,显然要么产生一个 3113 状物然后两侧必然独立,要么就是 11 之后两侧就独立了。
考虑独立之后我们只剩下两种数对,尝试进一步解决子问题。
不妨先考虑包含第二类数对的情况:注意到 2222 显然是一种独立的模式,而 232?3? 可以通过在问号处填入第三类数对的方式无限延长,最后以一个第二类数对结束,也是独立的。总而言之我们可以认为 \(\forall k\in \N\),\(k\) 个第三类数对和 \(2\) 个第二类数对可以拼在一起构成一个独立的模块。
然后考虑不包含第二类数对,即只包含第三类数对的情况:显然只有 333333 一种独立的模块。
由此,我们存在 \(3\) 种常数长度的独立模块,还有一种规律的模块。考虑我们只需知道每种不同的模块多重集下模块的总数 \(c\) 就可以直接对答案计入一个多重集排列数:模块之间必然独立,不存在第二种可能的划分,所以我们直接把模块一个一个排好就能得到一种方案,但是相同的模块之间没有区别。
于是考虑简单地使用组合而非 dp 的方法来计数:不妨枚举 3113 的数量和 333333 的数量,那么我们可以简单地算出还有多少个第一类数对,以及有多少种 11 的模块。然后考虑我们怎么把 232...232 嵌进去。先不妨假设这种都是 2222,对这四种简单的模块实施多重集排列数,然后再把多出来的第三类数对嵌入 2222。嵌入显然是一个插板。
CF704D. Captain America
考虑一个最典的连边方式:我们把行拆成一列点,列拆成一列点,然后把所有点先干成一种颜色,用连边来表示转化为另一种颜色,以及在源点和终点分开限制行列即可。
于是只需跑上下界有源汇费用流……吗?考虑我们钦定它的颜色是较大的一种,那么我们希望更多的点的颜色是较小的,也就是更多的点是变换颜色的,于是只需跑上下界有源汇最大流即可。
细节特别多。首先是我不会写板子了……然后就又去看了一遍板子。然后就是加边的时候需要注意合并限制,不合并限制会产生很多上下界的边,实际上只有一个限制,但是都去符合小限制,大限制流不动就寄了。
对着警示后人调了调还是调过了。
P8386 [PA 2021] Od deski do deski
比较反套路的是,判定序列合法实际上并没有简单的条件,也没有办法简单地钦定。那么硬着头皮考虑我们如何判定一个序列合法:这需要一个 \(\mathcal O(n^2)\) 的 dp 来进行序列划分。
不妨据此设计状态,我们实际上在一个位置上只关心是否 append 一个数之后序列的状态变为合法或变为不合法。那么显然我们需要维护一个维度 \(j\) 表示有多少个值 \(j\) 使得存在一个 \(j\) 的前一位可以完成一个划分。或者说算是类似 ddp,我们把判定的 dp 中 dp 值为 \(1\) 的部分塞进来了,但是我们根本没必要塞这么多东西,因为我们不在乎位置只在乎值,每种值又都是等价的效果。
于是我们直接 \(f_{i,j,0/1}\) 表示长度为 \(i\) 的前缀中,有 \(j\) 种值存在一个位置的前一位是可划分掉的,\(0/1\) 表示是否合法。据此初值、转移和答案都是容易的。
P10716 【MX-X1-T4】「KDOI-05」简单的字符串问题
非常简单的题。但是我困得连调和级数都没看出来呵呵。
注意到 \(A\) 是一个 border,容易想到 fail 树,答案都排在一条根到 \(i\) 的链上。然后显然答案具有单调性,于是我们只需二分即可。
那么现在我们只需判定 \(A\) 能否在 \([1,i]\) 中不重叠地放入 \(k\) 次。显然贪心是正确的,每次只需找一个最近的位置塞进去。可以发现不管 \(i\) 是多少,塞法都是一样的,并且注意到每个串 \(S[1,i]\) 至多塞 \(\frac{n}{i}\) 次,所以我们完全可以尝试预处理出每个串塞的位置,查询时二分。预处理时间复杂度为调和级数。
于是只剩下一个问题,每次我们要从一个位置快速向后找到一个和 \(A\) 匹配的地方,即下一个塞的位置。
一个办法是把 fail 树的子树拍平,然后在每次都是区间内做 lower_bound,显然可以写出一个 \(1\log\) 的线段树二分。
另一个办法是但是我们也可以不按照正常顺序来,像这种子树问题我们可以尝试按照 fail 树的 dfs 顺序进行计算,可以发现子树信息其实可以直接启发式合并 set 弄出来,所以直接在 set 里面跳就行了。
还有一个办法是,我们发现匹配具有单调性,一个地方开头能匹配的前缀是单调的,所以我们可以哈希计算每个 \(S[i,n]\) 和 \(S[1,i]\) 的 LCP,它的贡献最多只能到这个 LCP。所以我们也没必要搞什么启发式合并 set,直接离线扫前缀维护 set 即可。不过呢这里既然已经有 fail 树了那么启发式合并 set 会好写一些。
总时间复杂度 \(\mathcal O(n\log^2n)\)。
碎碎念:CF1968G2 Division + LCP (hard version) 和这个题完全是一个技巧…… 显然这个题也是我们计算每个前缀的最大分段数(即最大不重叠出现次数)对一段 \(k\) 贡献。
P12459 [JOI2025 预选赛 R2] 亲密的厨师
糖题,妈的没看到数据范围。
注意到 \(X_k\le 4\times 10^5\),考虑离线后应用 trick from merging。考虑任意一对人都只能贡献一次,\(i<j\) 没什么好处,不妨钦定是在 \(a_i>a_j\) 时贡献,特别地若 \(a_i=a_j\) 我们可以只考虑 \(i>j\)。
不妨先将 \(a\) 按上述方式排序,那么我们每个 \(a\) 能贡献的就是前缀。进一步考虑每个 \(a_i\) 对 \(b\) 排序后产生的 \(a_i+\max(b_i,b_j)\) 单调,所以可以多路归并。插入的时候需要快速跳过一下 \(i<j\) 的连续段和那 \(M\) 个不行的。
要找每个点后方第一个 \(i>j\) 的数只需使用主席树即可。
P12444 [COTS 2025] 发好奖 / Hijerarhija
树上依赖性背包 trick。
考虑一个显然的树形背包 dp,\(f_{i,j}\) 表示 \(i\) 子树内发了 \(j\) 元产生的最大提高,那么合并子树时产生的是一个树形背包,而且第二维与 \(siz\) 无关,我们的复杂度就是严格 \(\mathcal O(nk^2)\) 的。
这个背包是一个无性质的 \((\max,+)\) 卷积,无法做。
考虑如何规避合并背包。
我们应用一个很牛的技巧。这个题考虑有什么特殊性:如果我们一个点不选那么整个子树都似了。所以我们考虑在 dfs 序意义上做这个 dp,一因为个点选不选其实大部分来说是独立的,甚至和树的结构都没有任何关系,我们只关心它到祖先是不是全选的。
所以我们考虑按照 dfs 序 dp,每次选择有两种:不选这个点,那么直接跳到 \(dfn+siz\);选择这个点,那么进入 \(dfn+1\) 继续。考虑我们能到达一个点的决策肯定有它到根的路径都是选上了的(这条路径上的点的 \(dfn\) 肯定都比它小),所以没问题。
也可以 dfs 地写成从根向儿子转移的 dp。这种 trick 常见于统计包含根的连通块信息。
\(\to\) P6326 Shopping
考虑这里我们是选一个连通块使得连通块的重量 \(\le m\),价值和最大。其中每个点上挂着一个多重背包,可以使用二进制分组转化一下变成 \(\log\) 个物品的 01 背包。
应用类似的技巧,但是考虑我们现在可以允许任意一个点做根了。如果对每个点做根都跑一次显然很愚蠢,并且复杂度是 \(\mathcal O(Tn^2m\log V)\) 的显然跑不过。
考虑我们怎样使得不对每个根都跑一次。但是我们发现上面的技巧应用范围很窄,我们只能钦定一个点为根,用 dfs 序的性质来限制连通性,而无法在过程中选择根(这样会需要限制子树内部性,而这很困难)。于是我们考虑连通块统计有没有技巧,因为我们本质上的问题其实是同一个连通块每个点都算了一次,有没有什么办法可以避免反复算相同连通块呢?
有的兄弟有的,连通块计数/最值问题,考虑点分治 很古老了!完全没想起来。我们每次只统计包含分治中心的连通块即可,这样就避免了反复算相同连通块,恰好也满足我们的算法。
时间复杂度 \(\mathcal O(Tnm\log n\log V)\),二进制分组因为要钦定必选所以特别难写,并且慢的飞起……
P12449 [COTS 2025] 吸尘 / Usisavač
猜上下界不削能玩???我还以为是什么逆天数据结构题。
不妨设询问为 \(R\)。考虑显然的转化:我们实际上是选择一堆点 \(\{p_n\}\),表示在这些点要连接插座。这些点的 \(R\)-邻域必须要覆盖整棵树。
考虑此时答案是非常容易计算的:我们必须要走一遍 \(\{p_n\}\cup \{1\}\) 的虚树(不妨假设有 \(t\) 条边),还要把每条边都清洁一遍。容易证明每条边一定可以被恰好清洁到一次,所以算上各种走回去,这部分一共贡献 \(2(n-1+t)\)(清洁完走回插座导致每条边走两次,以及在插座之间移动导致虚树上的边还要多两次)。
但是这样还不对,我们最后一次可以不用走回去,可以省掉一次走回插座以及在虚树上走回根的过程。所以我们不妨设在 \(p_i\) 为插座的过程中距离 \(p_i\) 的最远点是 \(r_i\),实际上就是可以挑一个点 \(p_i\) 使得 \(\operatorname{dis}(1,p_i)+r_i\) 最大给它扣掉。
考虑如果总是有 \(p_i\to r_i\) 是一条由上至下的路径的话,我们容易发现 \(\operatorname{dis}(1,p_i)+r_i\) 就是距离 \(1\) 最远的点。其实我们也容易猜到这可能是对的,我们天然地觉得插座应该距离叶子 \(r_i\) 这样看着就不绕路特别好() 下面我们证明一下这个加上前面那个 \(t\) 就是整个题的下界。
我们先考虑 \(t\) 的下界。考虑显然的一件事:如果一条边的下端距离某个叶子的距离 \(\ge R\),那么它一定会被选中在虚树里面,因为它下面肯定有个插座。考虑又显然有一种构造可以取到这个下界:直接在这些边构成的虚树的叶子处放置使用的插座,虚树里面每个插座都“用”,这样我们显然一定可以覆盖掉整棵树。
此时我们发现这个东西的构造导致后半截就是我们所说的距离 \(1\) 最远的点的距离。我们考虑证明这是最优的。不妨调整已有的方案,比如尝试把要用的插座向下移动。可以发现这样 \(t\) 一定增加了 \(1\)(如果移动过了一条本来就在虚树上的,注意我们认为虚树上充斥着插座,没有影响),但是后面两坨最多增加 \(2\)(它不一定最大,即便是最大移动之后 \(r_i\) 也不一定是要往回走的),所以答案一定不会变优了。
P12429 [BalticOI 2025] Developer
我草你们 dp 优化无敌了。这玩意也他妈是题啊?
考虑显然的 \(\mathcal O(nV)\) dp,\(f_{i,j,0/1}\) 表示第 \(i\) 位现在的高度是 \(j\),上一个和它高度不一样的和它的大小关系是啥的最少改动次数。
然后容易想到 WC2025 T3,那个题里面我们的 \(j\) 只取 \(a_i\) 或者 \(a_i-1\),因为我们永远只会想刀掉一个不要的人,或者放过一个重要的人,不会刀一半不刀了或者多刀几下。这个题类似,我们的判定只关心大小关系,所以我们不会把楼盖到一个附近没有 \(a_i\) 的高度(准确来说,不是 \(a_i-1,a_i,a_i+1\) 中的任何一个高度),感觉一下那种高度肯定可以调整成一个附近的高度也没事。
前缀和优化,然后获得了 \(\mathcal O(n^2)\) 的分数!很牛啊!
搞出来的式子显然无法整体 dp,但我会猜结论!感受一下这个东西,你会觉得局部的一个东西改成一个很遥远的高度 \(a_i\) 的附近是比较搞笑的,因为我们其实只关注这周围的正确性。事实也的确如此,我们在 \(i\) 处只需转移给 \(j\in[i-4,i+4]\) 的 \(a_j-1,a_j,a_j+1\) 就可以了!
证明懒得看了,看了也没用。
P12401 [COI 2025] 玻利维亚 / Bolivija
我是唐诗。
容易发现的是,我们的合法子区间一定产生自对称层的极长区间,考虑如果中间有非对称层就直接倒闭了,而对称层的极长区间内部任取。所以我们可以轻易获得 \(63\) 分:等价于单点修改或没有修改,所以使用线段树维护极长 \(0\) 区间的长度的 \(\sum \frac{n(n+1)}{2}\) 即可。
进一步地,考虑每一层进行桶哈希,然后维护每一层两侧的哈希之差,那么一次变化相当于一个层区间的哈希同时加上一个数再取模。我们同样只需要找出极长 \(0\) 区间的长度的 \(\sum \frac{n(n+1)}{2}\)。分块加哈希表可以做到 \(\mathcal O(q\sqrt V)\),但是很遗憾,哈希表常数太大了,拼尽全力卡常无法进入 \(2\) 秒。
考虑不用哈希刻画这个回文状物,因为我们这里实际上比回文弱太多了,我们甚至写的是桶哈希。所以我们实际上只需维护每一层上不合法的位置数量(即对称差),那么每次只需要对 \(\mathcal O(1)\) 个区间的这玩意进行 \(\pm 1\),同样是查询极长 \(0\) 区间的长度的 \(\sum \frac{n(n+1)}{2}\)。考虑经典 trick from CF997E Good Subsegments,我们不好在别的操作的同时维护 \(0\) 的信息,但是我们可以方便地维护最小值的信息,而 \(0\) 就是最小值,我们当然不会进行不合法的操作。所以我们这里也同样维护最小值信息即可。
时间复杂度 \(\mathcal O(q\log V)\)。
P12446 [COTS 2025] 答好位 / Vrsta
构式交互题,很难啊我觉得。
考虑我们如何获得区间 \([1,n]\) 的最大值位置:我们只需要按照次大值分治下去造笛卡尔树就行了,使用询问 \([l,mid]\) 或者 \([mid,r]\) 的方式,判断下标是否是 \(mid\) 就能简单地获得最大值的方向,回溯上来就行。
然后考虑对最大值再造一次笛卡尔树,问题是我们又怎么获得新的区间的最大值。考虑一个聪明的办法,询问区间 \([l,mid]\) 获得的直接就是 \([l,mid)\) 的次大值,右侧同理,于是我们又可以造出最大值笛卡尔树。
现在我们已经花费了 \(2n\)(笛卡尔树一个 \(n\),获得最大值是每个点两次询问,至多 \(\frac{n}{2}\) 个点),考虑查询 \([a,b]\) 怎么办。可以发现整个问题几乎是不可合并的,所以我们不能用笛卡尔树的朴素合并办法来查次大值。考虑笛卡尔树的性质:\([a,b]\) 的最大值就在 \(a,b\) 这两片叶子的 LCA 上。那么考虑次大值其实就是 \([a,mid)\) 和 \((mid,b]\) 中的最大值的最大值。显然这个值也一定在 \(mid-1\) 和 \(mid+1\) 到这里的路径上。所以我们的操作指南应该是把询问“挂”在 \(mid\) 上(实际操作是预处理所有询问的答案),然后查询的时候直接查表。
考虑这种“挂”的操作需要我们求出 \(mid-1\) 和 \(mid+1\) 到 \(mid\) 这两条路径的序关系,这样我们查询的时候可以直接查序上第一个在 \([a,b]\) 区间中的位置。显然考虑归并两条链。
考虑怎么查询两条路径两个元素 \(x,y(p_x<p_y)\) 之间的大小关系。容易发现,若 \([x,y]\) 中存在一个数介于 \(p_x,p_y\) 之间,那么一定有一方不在到 \(mid-1\) 或 \(mid+1\) 的路径上,因为这两个点和 \(mid\) 贴死,如果中间有一个数更大,那么我们一定会经过那个数,并从那个数向中间靠拢,而不是分开;若 \([x,y]\) 中存在一个数比 \(p_x,p_y\) 都大就更不合理了。
于是我们只需直接查询 \([x,y]\) 就能获得次大值,即两者中较大的一个。
然后考虑证明归并排序的询问次数正确。因为归并排序本身是 \(\mathcal O(n)\) 的,我们只需考虑所有归并排序的链的长度和。显然每个点至多在它左右第一个比它大的点作为根产生的链中产生贡献,所以链的长度和是 \(2n\)。
总次数刚好 \(4n\),实际上显然完全跑不满。
疑似用单调栈可以更简单地理解。
实现时注意到 \(mid-1\) 一定是 \(mid\) 的前驱,\(mid+1\) 一定是 \(mid\) 的后继,可以把找路径写得更方便。
CF1450C1 Errich-Tac-Toe (Easy Version)
构造题人能做啊?
考虑完全没有任何思路。但我们只是需要每三个没有连续的,联想到棋盘的黑白染色的性质:两个没有连续的。所以我们考虑一种满足类似性质的三染色,使得任意一个长度为 \(3\) 的连续段没有同色的,那么我们只需将某种颜色全部改为 O,就能保证任意连续 \(3\) 个不可能有三个 X。
显然根据抽屉原理,总是存在一种颜色只有不超过 \(\left\lfloor\frac{m}{3}\right\rfloor\) 次出现。
那么我们考虑怎么染色,可以注意到 \((i+j)\bmod 3\) 是一种正确的染色。
CF1450C2 Errich-Tac-Toe (Hard Version)
延续 C1 的思路,我们只需对 X 和 O 分别选择一种颜色反转就行了。
然而有问题,我们不能选择相同的颜色,否则 XXXOOO 就暴毙了。不过可以发现只需对两种棋子分别数数量,取最小的不同的一对即可,考虑只有三种不同的颜色对,所以任取一种都只有 \(\left\lfloor\frac{m}{3}\right\rfloor\)。
P12196 [NOISG 2025 Prelim] Lasers 2
感觉大部分人,包括我做这道题都会分析一些看起来很有用但是实际没啥用的性质。
考虑一个起点是,我们暴力一个不移动的子集,那么我们的贡献将至少包括这些东西的并,然后考虑剩下可以移动的部分,显然我们只需考虑剩下部分的最大值放在哪里。
然后可能会觉得这个事情很麻烦,因为这个东西的放法看似没有什么很能简化 dp 的性质。实际上非常地愚蠢。我们只需考虑它是否是全局最大值就可以了。(我感觉我见过这个诈骗。但是忘记是哪个题了。)
如果不是全局最大值,那么全局最大值被锁定了,剩下的所有东西都能藏到全局最大值背后去,于是我们实际上被遮挡的只有所有锁定的区间的并。
考虑 dp 这个东西。首先交换状态和答案,\(f_{i,j}\) 表示前缀 \(i\) 中有 \(j\) 个活着的激光的最小花费。然后考虑每次将一个区间的合并进来维护并是不太好做的,所以我们考虑一次性跳过一整个连续段。不妨设 \(f_{i,j}\) 表示前缀 \(i\) 中有 \(j\) 个活着的激光的最小花费,且钦定 \(i\) 激光活着的最小花费。考虑我们从一个 \(f_{k,j-1}\) 转移过来,要求 \(i,k\) 不能在最长区间中(因为我们在这种情况中钦定它锁定),并且 \((k,i)\) 中任何一个相交不包含区间都要挪开,为了避免算重我们在左端点处计入,也就是 \(k+1\le l\le i\le r\) 的区间要挪开。
定义 \(g_{i,j}\) 表示 \(i\le l\le j\le r\) 的区间的 \(c_i\) 之和,那么转移就是:
差分 \(g_{k+1,i}\),即把它拆成 \(suf_{k+1}\)(\(k+1\le l\) 的区间数量)减去 \(k+1\le l\le r< i\) 的区间的数量。后者即为被 \((k,i)\) 包含的区间的数量。使用线段树区间加减来维护前缀 \(\min\) 即可。
说了半天那么如果全局最大值被解锁了怎么办呢。容易发现我们上述的 dp 其实根本没有在意区间的最终形态,只是在一味地挪开,然后把空留出来,换句话说只是因为最长区间的存在保证我们任意的挪开都合法,所以我们只需对着挪开进行 dp。
所以实际上全局最大值被解锁了,只是要求我们最后要留一个长度为 \(mx\) 的区间里面没有任何激光存在,这样我们把它放过去之后所有的挪开又都合法了。于是给 dp 加一维 \(0/1\) 表示是否已经留出就好了。
显然如果有多个最大值那么我们随便选一个都对。
剩下的就是实现了……感觉好难写。
也还好。这玩意卡常!加些没用的优化就过了。
P12357 [eJOI 2024] 贸易搭配 / Many Pairs
糖糖糖。为什么真的一道紫题都不会做啊!
考虑我们在树上枚举根,然后选择周围的两个儿子,显然有数种情况需要统计:
- 选到父亲
- 直接枚举再选了哪个儿子,那么实际上是三坨东西的并,我们可以直接把三个二维偏序拼起来。
- 选两个儿子
- 考虑我们简单地可以统计两个点都在同一儿子子树内部的贡献,主要考虑跨越的怎么办。看上去根本做不了,但实际上这是一个复杂度诈骗!在这种情况中,任何一个点对只会在 LCA 处贡献。所以实际上真正有跨越贡献的只有 \(\mathcal O(k)\) 个,对于其它的,我们可以简单地算出子树内贡献然后取前二大,有跨越贡献的挂在 LCA 上单独处理 \(\mathcal O(k)\) 次贡献即可。
注意要算一下从根出发的贡献。
被骗了。
UOI 2025
板刷一下,记录一下思维过程。 主播一道题都没做出来。 主播手刃了 Simple Subsequence(D1T4)。
P12544 [UOI 2025] Boys and Girls
对 \((a_i,b_i)\) 连边,边权为 \(c_i\),容易发现答案要么是一个点周围的边权和,要么是一个三元环。
前者显然很简单,我们考虑三元环怎么办。众所周知这玩意有一个 \(\mathcal O(n\sqrt n)\) 的算法。
考虑对 \(deg\) 根号分治,然后考虑三元环中三个点的情况。我们不妨称 \(deg>\sqrt n\) 的点为重点,其他点是轻点。显然至多有 \(\sqrt n\) 个重点。
- 三元环如果由三个重点组成,则我们可以直接枚举三个重点,用哈希表判断边存在性,复杂度 \(\mathcal O(n\sqrt n)\)。
- 三元环包含至少一个轻点,则我们可以枚举轻点,随后 \(\mathcal O(deg^2)\) 地枚举它的两条出边,同样是用哈希表判断边存在性,复杂度 \(\mathcal O(n\sqrt n)\)。
这里是最值问题,没有计数,所以无需进行容斥钦定。
你说得对,但是这个题有 \(2n\) 个点所以根本跑不动。
考虑这个题的好处是图很稀疏,所以我们尝试枚举其中一条边。继续考虑根号算法,如果两个点中有一个轻点,那么我们直接枚举轻点的出边;如果两个点都是重点,则直接枚举另一个重点。这样常数就小了很多。
还是过不了!打开题解看看有什么高见:卡常。
将所有 vector 取缔掉!还是过不了。
盲猜根号哈希表一辈子过不去,说明肯定有一个正常的做法不需要哈希表。有没有什么办法不用哈希表的?
有的兄弟,有的。这算是个精妙的科技。
考虑给图定向成一个 DAG,\(deg\) 小的点连向 \(deg\) 大的点,如果相等就考虑编号。显然任意一个DAG“三元环”都形如 \(u\to v,v\to w,u\to w\)。
考虑枚举 \(u\to v\) 这条边,再枚举 \(v\to w\) 这条边。我们可以在枚举 \(u\) 的出边的时候提前搞一遍,把所有出点打上标记,这样就可以轻松鉴定 \(u\to w\) 是否可行了。
显然复杂度是 \(\sum\limits_{(u,v)\in E} out_v\)。下面贴一个非常精妙的复杂度证明:

注意链式前向星 cache miss 太严重,改回 vector 才能过。
P12545 [UOI 2025] Partitioning into Three
先破环为链,那么相当于在 \([l,l+n-1]\) 考虑这个划分。
你先考虑划分成两个怎么做。题目中有明确的单调性指引,于是可以发现一种办法是使用双指针,我们枚举 \(l,r\),注意到我们可以钦定 \([l,r]\) 是较大者,所以我们希望 \((r,l+n-1]\) 尽可能大,但是不要超过 \([l,r]\)。显然随着 \(l\) 后退,\((r,l+n-1]\) 在增大,唯一的可能就是大过分了,所以 \(r\) 也只会后移,双指针成立。
继承这个做法,考虑三指针。我们仍然钦定 \([l,r_1]\) 是最大的,考虑 \(l\) 后移对 \(r_2\) 产生的影响。首先为了保证 \([l,r_1]\) 最大,\(r_1\) 肯定是也是单调向后的,于是 \((r_1,r_2]\) 会缩小。总而言之,\((r_1,r_2]\) 缩小了,而 \((r_2,l+n-1]\) 增大了,而我们希望让这两者的最小值尽可能大,所以会使得它们趋于平衡,所以 \(r_2\) 也会后移。
三个指针都单调,直接三指针即可 \(\mathcal O(n)\),移动顺序上,我们在最外层移动 \(l\),之后显然应该先移动 \(r_2\) 再移动 \(r_1\)(因为 \(r_1\) 的判定由 \(r_2\) 的最佳位置获得)。注意一下区间非空。其实还有一些很神秘的细节问题没有搞清楚(条件上的取等),但是写过了。
P12546 [UOI 2025] Convex Array

差分单调不递减。
实际上显然解由一个单调不减一个单调不增序列构成,其中最底部显然一定是全局 \(\min\)。
考虑从 \(\min\) 的角度出发,它只能看到两个单调不减且差分递增的序列。换句话说,我们只需对元素排序,判断能否将 \(\min\) 之后的 \(n-1\) 个元素划分为两个子序列使得两个子序列差分递增。
贪心甲烷了,考虑 dp。
\(\mathcal O(n^2)\) 的 dp 是比较简单的。
先不妨设 \(f_{i,j}\) 表示当前在 \(i\),另一个序列的上一个元素在 \(j\),另一个序列的最小差分。只需看最终是否有 \(f_n\ne +\infty\) 即可。
然而你发现我们无法转移。考虑 \(f_{i,j}\to f_{i+1,i}\) 的过程,即 append 另一个序列,此时我们无法更新答案,因为我们不知道 \(i\) 处当前两个点的差分。
考虑优化信息的记录。可以发现两个序列当中总有一个上一个元素是 \(i-1\),所以实际上 \(i-1\) 帮我们维护了其中一个序列的前一项。因此不妨设 \(f_{i,0/1,j}\) 表示当前在 \(i\),\(i-1\) 属于当前还是对面序列,\(j\) 为与 \(i-1\) 不同归属的上一个元素的位置,此时另一个序列的最小差分。
使用滚动数组之后通过了 \(63\) 分。
尝试乱搞,假设 \(j\) 距离 \(i\) 不太远。显然是错的。
投降,看看 Anton 有什么高见。
Solution
整体 dp 领域大神。
考虑我们现在的 dp 只用到了 \(i-1\) 的信息。这不够。我们再往前推一步,考虑 \(i-2\)。
那么此时 \(i\) 处的状态就更有说法了。容易发现只有以下三种:
- \((i,i-1)(b,c)\)。
- \((i,i-2)(i-1,c)\)。
- \((i,c)(i-1,i-2)\)。
后两者我们显然希望最大化 \(c\),所以可以把这玩意塞进答案,使用 \(\mathcal O(n)\) 的状态即可。但是第一种我们无论如何都要设计出 \(\mathcal O(n^2)\) 个状态,即便把一个维度贪心地塞入答案。
所以主要考虑第一种怎样转移。
考虑如果它往第一个序列里面 append,那么实际上转移到 \((i+1,i)(b,c)\),合法的 \((b,c)\) 的状态没有变化。只需满足 \(a_{i+1}-a_i\ge a_i-a_{i-1}\) 即可。
考虑如果它往第二个序列里面 append,那么实际上转移到 \((i+1,b)(i-1,i-2)\),也就是第三种状态。只需满足 \(a_{i+1}-a_b\ge a_b-a_c\),也即 \(a_{i+1}\ge 2a_b-a_c\)。
考虑整体 dp。第一种转移是继承性的等于没有转移,第二种转移我们只在乎 \(a_{i+1}\ge 2a_b-a_c\) 的状态中 \(b\) 最大的一个。可以发现这个思路的本质并没有减少状态数,但是我们修改了状态使得状态便于使用整体 dp 了:我们将 \(b\) 塞入答案,状态是 \((i,i-1)(2a_b-a_c)\)。这样我们可以用任何数据结构,在 \(2a_b-a_c\) 下标处维护它对应的 \(b\) 的最大值,第一种转移的时候要么继承要么清空,第三种转移的时候查询一下前缀 \(\max\)。
第二种状态和第三种状态则可以直接做,他们对第一种状态的转移是 \(\mathcal O(1)\) 的,类似一个单点修改。
接下来考虑怎么规避大型数据结构。考虑我们只需要维护快速清空、单点增大、查询前缀 \(\max\)。

考虑 std::set。我们发现将操作削弱到单点增大之后,维护前缀 \(\max\) 实际上是一个区间推平。于是考虑直接类似 ODT 操作,只保留前缀 \(\max\) 上的突变点即可,查询前缀 \(\max\) 时我们只需要 lower_bound。复杂度均摊。
P12547 [UOI 2025] Simple Subsequence
你怎么降紫了啊? 确实不黑。
状态不太好,还读错了一次题,想了比较久。其实不太难想?
考虑第一步转化,画成折线之后容易发现好的序列等价于 \(\min\) 在开头 \(\max\) 在结尾。
那么我们考虑先把所有 \(1\) 选上,然后考虑 \(-1\) 的问题。
先尝试限制 \(\min\) 在开头。毕竟开头必然是 \(0\) 限制起来比较轻松。容易发现不能加入的 \(-1\) 就是那些 \(i\) 处是一个全新的负前缀 \(\min\) 的位置。这是好理解的:考虑我们遇到第一个 \(presum=-1\) 的时候肯定要把它删掉,此时后方所有折线全部往上挪一格,于是下次只有 \(presum=-2\) 的时候才算数了。
考虑这个不太方便。进一步地其实我们可以发现这个所谓的负的前缀 \(\min\) 数量其实就是全局 \(\min\) 的绝对值(全局 \(\min\) 大于 \(0\) 除外),因为开头就是 \(0\),所以最小值的绝对值是多少就有多少个负的前缀 \(\min\)。
然后考虑限制 \(\max\) 在结尾。我们考虑先执行限制 \(\min\) 在开头(即上一步)的删 \(-1\) 操作,然后找出当前剩余序列的 \(\max\),它可能比最终的值高出一截,我们需要删去它之后的 \(-1\),显然需要删去 \(\max-sum\)(\(sum\) 表示当前序列的总和)个 \(-1\),就能让 \(sum\) 升起到和它一样高了。进一步地,由于上一步删去 \(-1\) 的数量固定,所以为了使得当前剩余序列的 \(\max\) 最小,我们总是在第一次遇到某个负前缀 \(\min\) 时就删去它。
现在唯一的问题是找出这个 \(\max\),因为 \(\min\) 和 \(sum\) 都是可以简单维护的。
用川子最喜欢用的形式化。这其实相当于我们有一个随机变量 \(x=0\),我们从 \(l\to r\) 扫,每次执行操作:
- \(x\to \max(x+a_i,0)\),和 \(0\) 取 \(\max\) 描述了删去新出现的负前缀 \(\min\)。
- \(mx\gets \max(mx,x)\)。
我们需要这个 \(mx\)。
考虑如何高效计算之。尝试把它转化为没有和 \(0\) 取 \(\max\) 的版本。可以发现,当 \(x\) 与 \(0\) 取 \(\max\) 时,我们相当于在切分子问题,因为对下一段来说,又变成了一个 \(x=0\) 从左往右扫的问题。
所以一个转化是,我们找到一个从 \(l-1\) 开始,\(sum\) 单调递减的序列,且序列中的每个元素都是尽量靠前的。那么我们实际上是相当于对这个序列的每一段做这个问题。
这样还是很难做。但是你发现这个东西有非常强大的不合法不优性质,因为最小值在不断变小,所以我们无需关心前面分段处的数具体是多少,只需一直对 \(sum\) 进行前缀 \(\min\) 然后求 \(sum_i-\min\) 的最值即可。
那么这个就很好做了,使用线段树即可。
P12548 [UOI 2025] Manhattan Pairing
主播的错误思路,并且还难写得要死
比较轻易的分类讨论可以证明,任何一条 \((x,0)\to (y,1)\) 配对的路径上不可能存在一个点 \((z,?)\) 满足 \(\min(x,y) < z < \max(x,y)\)。因为这样总可以通过撤销两个匹配重新来得到更小的最大值。
据此考虑这种配对产生的影响:可以发现这种配对实际上是把整个问题划分掉了,也即在任意相邻两个这种配对之间,一定是若干个两侧连续消化。
然后可以考虑 dp。只是说点重叠的时候有巨量麻烦的事情。
啊贪心 + dp 怎么这么难。
先判掉答案是 \(0\),使得我们可以随意地使用长度为 \(1\) 的匹配。
考虑二分之后使用贪心 check。
显然从左往右扫描线,显然地考虑我们维护一个东西叫前面剩下的没配对的点。显然只有至多 \(1\) 个点,考虑两个点的话本质上是在拉长它们之间的匹配,还不如让它们直接匹配了。
当我们抵达一个 \(x\) 的时候,显然我们应当立即匹配前面剩下那个点。但问题是,我们可能也要剩下一个点留给后面。
容易发现根据两侧点的奇偶性进行讨论,剩下一个点可能在 \(0\),可能在 \(1\),也可能两边都可以。维护三种状态的可行性与剩余点。
P12549 [UOI 2025] Gift for Anton
来点小清新构造题爽一爽。
容易感受到在网格图上不太可能有能用到 \(3,4\) 的结构:我们不能够构造出一个网格图的导出子图每个点都三度或者四度。
考虑我们有些啥。\(0\) 就是覆盖一个格子,\(1\) 就是覆盖相邻两个格子,\(2\) 就是一个简单环(corner case 除外)。
考虑 110110110... 这样的操作来铺满一行,容易发现我们可以取到任何值。麻烦的是下一行我们无法放入任何横着的 11,但是换个角度,我们可以竖过来。考虑构造:
221221...
221221...
其中 11 要对应前一行的 0。这样我们就可以向下铺满两行。
这两个结构可以交错放置,容易发现类似 110 的这也是长度为 \(2\) 和长度为 \(1\) 的结构,所以我们也可以取到任何值。就做完了。
P12550 [UOI 2025] Reversal ABC
手玩一下这个交换容易发现,当我们交换一对之后,这两个数会彼此锁定。比如 \(\texttt{AB}\) 交换之后,左边的 \(\texttt{B}\) 不可能凑出 \(\texttt{BC}\),右边的 \(\texttt{A}\) 不可能凑出 \(\texttt{CA}\),于是它们就锁死了。
进一步地,我们发现左边的 \(\texttt{B}\) 仍然可以移动,但是不能突破左边的第一个 \(\texttt{C}\),右边的 \(\texttt{A}\) 也同理。扩展这个性质到操作上,实际上我们是可以选中一串只含两种字母的子串然后在里面邻项交换,直到某种靠后的字母全部在另一种前面就倒闭了。
根据冒泡排序结论,这个交换次数是固定的,我们只能交换“逆序对”(\(\texttt{AB},\texttt{BC},\texttt{CA}\) 子序列)数量次。
由于我们每次会把一个子串交换到锁死状态,所以子串的结构之间没有影响。所以我们实际上是对这个串进行划分,使得所有段的“逆序对”数量和最大。直接 dp 就是 \(\mathcal O(n^2)\) 的。
考虑优化。我们先证明几个结论:
Lemma 1. 不存在一种优的划分,划分两边的字符相同。
Proof. 考虑这个切点所在的极长连续段,现在我们把它分成了左半边和右半边,在两个段分别贡献“逆序对”。而总有一边贡献得多一点,我们把切点往少的那边挪,让多的那边字符多一些总是不劣的,所以我们一定可以把切点移到连续段的端点处。
Lemma 2. 不存在一种优的划分,有相邻两个段当中出现的字符集合相同。
Proof. 显然,把这两个段合并在一起一定会让“逆序对”数量增多。
据此我们考虑转移 \(f_i\) 的时候有哪些决策点是有用的。我们考虑二分(双指针也可以)找出最远的一个 \(j\) 满足 \((j,i]\) 只有两种字符,那么根据上述结论,决策点一定在 \(j+1\) 所在连续段的端点上(在内部不满足 Lemma 1,再往后移不满足 Lemma 2)。
于是我们把这两个点拿出来转移就可以了。使用两个前缀和计算转移时产生的逆序对即可。
用双指针写即可做到 \(\mathcal O(n)\),\(\Sigma\) 几乎是常数所以省去了。效率甩斜率优化几条街。
P12551 [UOI 2025] Simple Task
只是一味地难写和分类讨论。
介绍一下这个题在奇数时最关键的结论:我们一定会尝试先分一组大小为 \(3\) 的质数出来构成一个合数,其中奇质数的数量是奇数个。这样做之后奇质数数量就会变成偶数,我们就可以两两配对了。
考虑证明为什么我们不会分成 \(5\) 个?其实是可能的,但是这种可能产生自其中只有一个奇质数,剩下的都是 \(2\)。换句话说,我们不可能选择 \(5\) 个及以上奇质数。这是因为任何 \(\ge 5\) 个奇质数都一定存在一种方法选择大小为 \(3\) 的子集构成一个合数。
Proof.
将奇质数按照 \(\bmod 3\) 分类。根据抽屉原理,\(5\) 个数时一定要么存在一种余数出现了 \(3\) 次,要么 \(0,1,2\) 三种余数同时存在。这两种情况都可以构造出 \(3|sum\)。
另一个结论是所有奇质数当中只有 \(3\) 需要 \(+6\) 才能变成合数,别的都最多只需要 \(+2\) 或 \(+4\)。证明考虑 \(\bmod 3\) 于是显然。
然后分讨就可以了。我奇数处写得有点烂。
单挑 GXCPC 2024
赛前 vp 一时爽,一直 vp 一直爽。
A. Ascending Random Number Sequence, +1
简单 dp,\(f_i\) 表示不在乎 \(k\),序列以 \(i\) 结尾的期望长度。为了维护这个长度的 \(+1\),还需要 dp 一下 \(g_i\) 表示序列以 \(i\) 结尾的概率。有 \(k\) 就钦定一下倒数第二位是谁就行了。
前缀和优化,然后滚动一下 dp 数组,求个线性逆元完事。不滚动会 MLE,样例罚时烦死了。
B. Black Friday, +
模拟。
[TBC]
P10253 说唱
根本不是人。
考虑重新表达 \(f(x)\)。考虑拆位,表达 \(x=\sum\limits_{i=0} 10^ix_i\),求和形如一个等比数列求和,每个 \(x_i\) 的贡献是 \(\frac{10^{i+1}-1}{9}\),乘开发现 \(f(x)=\frac{10x-\sum x_i}{9}\)。
显然这当中 \(\sum x_i\) 是可枚举的,考虑枚举它为 \(s\)。那么 \(x\) 可以被反解出来:\(x=\frac{9y+s}{10}\),我们只需检验 \(x\) 的数位和是不是 \(s\) 即可。
先用高精乘 \(9\) 得到 \(9y\),然后考虑一个一个加 \(1\)。显然复杂度是正确的:对所有位的操作次数是 \(n+\frac{n}{10}+\frac{n}{100}+\cdots\),其中 \(n\) 是位数,这个值显然不及 \(2n\)。在加 \(1\) 的过程中可以轻易维护数位和,于是实现简单。
镜子 (mirror)
先考虑怎么做到多项式复杂度。显然 \(f(x,y)\) 表示的是所有从 \((0,1)\) 走到 \((1,1)\),再走到 \((x,y)\) 的不经过障碍物的路径中最少的弯折次数。
容易想到,答案的结构一定形如从 \((1,1)\) 开始走到某个障碍的旁边贴着,中间一定是一条一次弯折或者没有弯折的路径。证明考虑调整,比较容易理解。
进一步地,如果这是一条没有弯折的路径,即直路,我们可以强行钦定它算成弯折。这是因为不合法不优,任何考虑到直路的路径一定可以被全部都是弯折来连接的路径替代掉。
考虑设计状态。我们只保留障碍物旁边的格子和 \((1,1)\),这样的格子只有 \(\mathcal O(k)\) 个(称为关键格子)。任意两个关键格子之间,只要存在一条弯折一次的路径(显然一共只有两条)不经过障碍就能直接进行花费 \(1\) 的转移(上文已经说过不用考虑是不是直路)。显然这样造出来的重构图,包含了到达这 \(\mathcal O(k)\) 个格子的最短路,可以求出它……吗?
并未包含。考虑我们在经过一个关键格子的时候,虽然两侧确实是两条弯折路径,但是不知道这个格子上是不是有弯折,这是需要处理的。所以我们要多出一倍点,表示在关键格子上的方向。两个钦定方向的关键格子之间显然至多有一条弯折恰好一次的路径,花费为 \(1\),而将一个关键格子处弯折,也需要花费为 \(1\) 的边。
据此对图进行 BFS,即可求出到达每个关键格子的每个方向的最短路,也就是最小花费。
现在考虑从关键格子扩展到所有点。考虑到达该点的答案路径,因为直路仍然不合法不优,一定也是从某个关键格子开始走一条弯折路径(除了第一列上和 \((1,1)\) 连通的点),所以我们只需找出所有到达它路径上没有障碍的关键格子中最短路最小的一个,再加 \(1\) 就可以了。最后需要抠掉第一列上和 \((1,1)\) 连通的点的错误贡献。
据此我们获得了一个 \(\mathcal O(n^2+n^2k+k^2)\) 的做法,跑 \(20\) 分都卡常真不知道有啥用。
考虑优化。容易通过交换枚举顺序,考虑每个关键格子对所有格子的贡献做到 \(\mathcal O(n^2+nk+k^2)\),可以通过 \(35\) 分。
进一步优化。此时我们已经非常接近正解了。考虑最短路的部分是最能优化的:因为 BFS,我们只需找出每次队首可以到达的点,暴力删掉就是均摊的。于是考虑哪些点会被队首到达,可以发现就是到队首的路径上没有障碍的点。
不妨假设队首的方向是左右,上下同理。考虑它的左右界是好求的,就是两端的障碍,我们需要求出这个区间内的所有列中有哪些列满足存在一个活着的格子可以不用经过障碍到达。
引用题解中的段理论。我们考虑如下事实:将所有障碍和它上下左右的第一个障碍匹配起来,可以获得 \(\mathcal O(k)\) 个段,其中有横着的也有竖着的。我们将这些段收缩一格,使得两端都是关键格子。
继续考虑我们刚刚的问题。可以发现我们每次站在队首 \((x,y)\) 这个格子,尝试左右移动再上下移动,实际上消灭了所有在左右界列内的(不要忘记左右还有障碍),竖向的 \(L\le a\le R\) 的段,显然这些段中的关键格子都不被遮挡(考虑一个段中只有两个关键格子,并且被穿过了),这就是我们需要的。所以暴力转移即可。要找出这些段只需在线段树上维护行一维和竖直段,然后用 set 树套树掉跨越每一行的段,只需要支持单点查询区间修改 set,标记永久化即可。列也是同理的。注意删除一个关键格子的时候没必要也去另一个方向删除和合并对应的段,只需要不转移就可以了,因为总的段数级别仍然是正确的。
注意每个段的两个关键格子最短路是相同的(除了第一列上与 \((1,1)\) 直接连接的一段,我们钦定其算成 \(0\) 就好了)。
于是 BFS 可以做到 \(\mathcal O(k\log^2 k)\)。
考虑优化计算答案。容易发现实际上我们原来那个东西本质上也是一个最短路,只不过是最后从一个关键格子扩展到任意格子的一步而已。在这里每个格子要么属于一个横段要么属于一个竖段,这个东西其实就是这两个段的最短路的 \(\min\)(注意此时对第一列特殊情况的处理也正确了,无需再特别减去)。考虑怎么做这个问题。
显然我们按照一种方向算完是简单的,但是需要解决另一种方向的贡献。
考虑一个显然但容易被忽略的性质:如果一个格子是在两个段的交上,那么两个段的关键格子的最短路差实际上最多只有 \(1\),证明显然:在这个格子上放个镜子就好。问题就在于怎么抠掉这个 \(1\) 的贡献。
考虑我们先快刀斩乱麻按照横着的段算了所有点的贡献(实际上不用再算一遍。注意到这个过程在 BFS 中已经顺便完成了,每一段的最短路就是),那么这些点一定满足在竖直段意义下是 \(dis_{\text{横段}}-1\)。
我们考虑从第一行开始扫描线。
在扫描线的过程中,我们用线段树维护每一行上每个点,在只考虑竖着的段的情况下的贡献。这只需要在扫描线过程中离线地插入 \(\mathcal O(k)\) 个竖段,显然是简单的。
然后考虑每一个横着的段,我们需要在扫描到它的那一行时计数它当中有多少个 \(dis-1\),把这种东西的贡献抠掉。利用经典 trick,这个值的数量不好数,可这个值是最小值,所以在线段树上维护最小值数量即可。
这部分复杂度是 \(\mathcal O((n+k)\log n)\)。
总复杂度 \(\mathcal O((n+k)\log^2n)\),常数起飞,怪不得开了 \(5\) 秒,我还以为是根号题。

浙公网安备 33010602011771号