Solution Set - “我献上明月一盏,照满河山”
- 0.「集训队互测 2018」「洛谷 P9248」完美的集合 ⭐
- 1.「UR #6」「UOJ #74」破解密码
- 2.「NOI Simu.」苯为
- 3.「NOI Simu.」西克
- 4.「NOI Simu.」尼特 ⭐
- 5.「UR #6」「UOJ #75」智商锁 ⭐
- 6.「UR #7」「UOJ #82」水题生成器
- 7.「NOI 2011」「洛谷 P1971」兔兔与蛋蛋游戏 ⭐
- 8.「BJOI 2018」「洛谷 P4428」二进制
- 9.「NOI 2012」「洛谷 P2179」骑行川藏
- 10.「UR #7」「UOJ #83」水题出题人 ⭐
- 11.「AGC 062A」Right Side Character
- 12.「AGC 062B」Split and Insert
- 13.「集训队互测 2015」「UOJ #91」最大异或和 ⭐
- 14.「集训队互测 2015」「UOJ #93」上帝之手 ⭐
- 15.「集训队互测 2015」「UOJ #92」小树苗与集合 ⭐
0.「集训队互测 2018」「洛谷 P9248」完美的集合 ⭐
- Link & Submission.
- 「A.DP-树上 DP」「A.数学-数论」
和希望一样的东西, 对 \(x\) 形成的连通块做点减边容斥. 此后, 我们需要对于一个根 \(r\), 求出包含 \(r\) 的集合信息. 这个集合信息是一个背包信息, 自然想到规避背包合并的 DFN DP. 做做就完了, 求一个 \(r\) 的答案是 \(\mathcal O(nm)\) 的.
求出全局最大权值, 在枚举每个根算方案数. 其中需要用到组合数, 这里需要一个 ex-Lucas 的变形.
- Ex-Lucas (\(m=p^k\), \(m\) 极大, \(p\) 极小).
还是和正常 ex-Lucas 一样, 难点在于算 \(n!\) 除去 \(p\) 因子的结果. 我们知道
\[n!/p^{w_p(n!)}=(n/p)!/p^{w_p((n/p)!)}\prod_{i=1,p\notin i}^ni. \]后者仍然类似阶乘, 所以可以用类似快速阶乘算法的东西算. 令 \(f_n(x)=\prod_{i=1,p\notin i}^n(x+i)\), 则 \([x^0]f_n(x)\) 是答案. \(x\) 的引入让位移倍增得以实现:
\[f_{2kp}(x)=f_{kp}(x)f_{kp}(x+kp). \]复合过程可以暴力进行, 因为本题 \(m=5^{23}\), 只需要保留次数低于 \(23\) 的系数. 最后 \(\mathcal O(p)\) 个剩下的东西暴力算就行. (\(m=p^k\), \(p\) 视为常数的语境下) 复杂度 \(\mathcal O(w^2\log^2n)\).
因此, 本题复杂度 \(\mathcal O(n^2m+nw^2\log^2(2^n))=\mathcal O(n^2m+n^3w^2)\), 其中 \(w=23\).
1.「UR #6」「UOJ #74」破解密码
- Link & Submission.
\(h_i\times26-h_{i+1}=(26^n-1)t_i\), 若 \(p\mid(26^n-1)\), 旋转后的关系恒成立, 只需要求 \(h_0\) 的在 \(26\) 进制下的串 (\(p\le26^n-1\), 一定存在); 否则可以直接解出所有 \(t\). \(\mathcal O(n)\).
2.「NOI Simu.」苯为
我们只关心最终大基环树的环长, 环长又由 \(d(s,t)\) 确定, 设 \(F(z)\) 为树上路径数量关于路径长度的 GF, 那么
数路径是 \(\mathcal O(n\log^2n)\) 的, 但 GF 代入了 \(z\) 值就变成 一个数啦, 像数路径一样点分治就 OK.
3.「NOI Simu.」西克
- Private link & Submission.
- 「A.数据结构-并查集」
也许复杂度正确的算法很好想, 但咱毕竟是写代码竞赛对不对? (
从下到上很简单, 随便倍增一下就行. 从上到下的话, 涉及到在儿子处划分许多询问的去向. 自然的想法是启发式分裂, 但这玩意儿确实不好写. 一个巧妙的一点的算法是撤销并查集, 对询问维护并查集, 并维护每个颜色的代表点, 这样可以完成进入结点时对颜色的合并. 我们没有分裂合并对询问的影响, 但只需要在出子树的时候把这些影响撤销掉就好, 反正被非法影响到的询问都在子树外, 不会被问出错误答案. 复杂度都是 \(\mathcal O(n\log n)\) (\(n,q\) 同阶).
4.「NOI Simu.」尼特 ⭐
- Private link | 胡!
- 「A.数学-生成函数」「B.Tricks」
直觉上, 我们只需要 \(a_i\overset{?}{=}a_{i+1}\) 这个信息, 不妨令 \(k_i=[a_i=a_{i+1}]\). 若随机串已知, 我们会从右到左依次枚举空格求答案, 每次答案的变化量在 \(\{-1,0,+1\}\) 中, 转化到网格图上, 就是从某个 \((0,y_0)\) 出发, 走 \(n\) 步, 求折线的最高纵坐标的过程. \(k_i=1\) 时过于平凡, 我们只需要讨论 \(k_i=0\) 的情况.
令 \(f(i,j)\) 表示放了 \(i\) 个箭头, 折线最高点与当前位置高差为 \(j\) 的方案数; \(g(i,j)\) 表示放了 \(i\) 个箭头, 折线最高点与当前位置高差为 \(j\) 的折线线最高点坐标之和. 那么:
令 \(F_i(z)=\sum_jf(i,|j-i|)z^j\), \(G_i(z)\) 同理 (这里比较 tricky), 那么
观察到
回到原问题, 我们需要对常数 \(c\) 求出 \(G_c(z)\) 的各项系数. 这里用 \(A(z)=B^k(z)\Rightarrow\frac{A'(z)}{A(z)}=\frac{kB'(z)}{B(z)}\) 对比系数即可. 复杂度 \(\mathcal O(n)\).
5.「UR #6」「UOJ #75」智商锁 ⭐
- Link & Submission.
- 「A.构造」
这是一道很 "OI" 的构造题, 比较容易创死喜欢数理上构造答案的生物. (
题解倒是很简单: 随机 \(10^3\) 个含 \(12\) 个结点的图 (每条边以 \(0.5\) 的概率存在), 矩阵树求出生成树数量, two pointers 查询四棵树 \((A,B,C,D)\), 使得 \(c_Ac_Bc_Cc_D\equiv k\pmod P\) 即可.
真敢呐.
6.「UR #7」「UOJ #82」水题生成器
- Link & Submission.
狭路相逢, 勇者胜! 每次从 \(n\) 到 \(1\) 枚举因子乘到减数上, 迭代, 过之. 正解是把阶乘进制倒过来算, 嗯嗯.
7.「NOI 2011」「洛谷 P1971」兔兔与蛋蛋游戏 ⭐
- Link & Submission.
- 「C.性质/结论」
二染色, 发现兔兔只可以将空格移到与初始位置异色的白子上, 蛋蛋同理. 不满足要求的棋子视作障碍, 移动后的棋子也是障碍, 那么这就变成了一个网格图上简单路径问题. 判定胜负得到解决: 检查是否存在除开起点的最大匹配.
移动的话, 就增量地尝试修改匹配就行, \(\mathcal O(nmk)\). (虽然一不小心发现每次暴力匈牙利也能过.)
8.「BJOI 2018」「洛谷 P4428」二进制
- Link & Submission.
哪来那么无聊的题… 显然区间非法条件是:
- 仅存在一个 \(1\);
- 存在奇数个且大于一个 \(1\), 但存在至多一个 \(0\).
然后 DDP 就行. \(\mathcal O((n+q\log n)w^3)\), \(w\) 是矩阵大小, 目测没什么特别优美的矩阵构造.
呃啊… 这东西写着真的讨厌. 我的写法是一个 \(4\times4\) 的矩阵算满足前一个条件的区间, \(6\times6\) 的矩阵算满足后一个条件的区间, 一车 BIT 维护同时满足两个的区间, 容斥容斥. 带着一堆 template
倒是也不难写.
9.「NOI 2012」「洛谷 P2179」骑行川藏
- Link & Submission.
- 「A.数学-Lagrange 乘子法」
属实没想到这个冷门 tag 会迎来第二题. 显然令总耗能恰好为 \(E\) 最优, 引入这一限制的辅助元 \(\lambda\), 则
导之,
注意下面这个三次方程的一阶导恒正, 因此我们并不需要学会解三次方程, 可以直接二分求 \(x_i\) 的数值解. 另外也可以观察到 \(\lambda\) 与实际 \(E\) 负相关, 所以外层二分一下 \(\lambda\) 即可. 二分的界就… 随便编吧. \(\mathcal O(n\log^2 V)\), 洛谷的数据对精度和范围的要求似乎比 LOJ 弱很多.
好像上一道这个 tag 的题也是二分 \(\lambda\) 控制合法性, 看来这个 trick 可以留意一下喵.
10.「UR #7」「UOJ #83」水题出题人 ⭐
- Link & Submission & Generator.
- 「A.构造」「C.非传统-提答题」
Subtask 1 (merge vs counting) 只需要一个大数, counting 就飞了.
Subtask 2 (bubble vs selection) 这俩复杂度一样, 但观察代码发现 bubble 的 swap 常数是 \(3\), selection 的交换常数是 \(2\), 我们不能让后者超过前者, 那就索性给一个有序序列! 当 \(n=1990\), 序列有序时, selection 的次数是 \(1998959\), bubble 的次数是 \(1985028\), 但我不能再增大 \(n\) 了, 看来还是需要引入交换. bubble 还能支撑 \((1986591-1985028)/3=521\) (今天的日期 w) 次 swap, \(521\times 2\) 次操作刚好可以让 selectoin T 掉! 我们只需要引入这 \(521\) 次 swap 即可.
Subtask 3 (merge vs quick) 你这快排有点假啊? 我们尝试每次把区间最值放在区间中点, 这样没有随机化的快排就成为尸体了. 具体来说, 每次我们一定是把区间中点换到左端点 (假设中点是最小值), 其他的不变, 这样 \(l=1\sim n\) 模拟一遍, 安排初始位置即可. 当然对 merge 的要求也很死, 手动二分 (迫真) 一下取 \(n=1984\) 刚好满足条件.
Subtask 4 (counting vs bubble) 肯定是一个降序序列, 为为了节约 counting 的枚举, 我们猜测序列形如 \(a\) 个 \(b-1\), \(a\) 个 \(b-2\), …, \(a\) 个 \(0\), 尝试后发现 \(a=b=32\) 的时候刚好杀掉 bubble, 但 counting 超了一点点. 于是又尝试 \(a=32\), \(b=31\), 最后补充 \(r\) 个 \(0\). 又一次手动二分后发现 \(r=20\) 刚刚好卡满 counting 的要求, 同时干掉了 bubble.
Subtask 5 (selection vs bubble) 刚刚嫌弃 bubble 常数大, 现在又觉得它常数不够大喵. 我们得构造一个最小值更换次数小但逆序对数大的序列. 一个可行的构造是令 \(f(l,r)=\lang p\rang+f(p+1,r)+f(l,p-1)\), 其中 \(p=(l+r)/2\), 这样 selection 的交换次数是 \(\mathcal O(n\log n)\) 的, 而逆序对仍然是 \(\mathcal O(n^2)\) 的. 取 \(f(0,1002)\) 即可.
Subtask 6 (bogo vs quick) 多少有点荒谬了, 我们势必要去研究 bogo 的随机数生成器的特殊性质. 可以发现, RNG_a
和 RNG_b
的最低 bit 都是 \(1\), 但次低 bit 却很大, 因而当 \(n=2^k\) 时, 第一次排序过程生成的下标是连续自然数! 不妨取 \(n=2^{12}\), 我们先枚举一个目标 seed
, 反向模拟 bogo 的操作还原初始序列, 然后检查 quick 会不会 T. 一组能够卡掉 quick 的数据是 \(\textit{seed}=2^{11}\). 接下来只需要构造一个相对顺序不变, 生成目标种子的序列, 暴力循环把 \(>a_{n-1}\) 的数 \(+1\), 计算为了达到种子, \(a_{n-1}\) 的理论取值, 检查是否符合条件即可. 带着 -O2
只需要大概 \(0.4\text s\) 出解, 可行的增量是 \(75883\).
11.「AGC 062A」Right Side Character
- Link & Submission.
首先, 全 A
和全 B
的串可以先判掉. 否则手玩一下发现有 BA
子串就剩下 A
, 否则剩下 B
.
12.「AGC 062B」Split and Insert
- Link & Submission.
- 「A.DP-区间 DP」「C.思维」
令原题中 \(k\to m\). 大概就是考验一个讲故事能力. 第一步转化自然是倒序操作, 现在每次操作可以把 \(p\) 的一个子序列拉到最后, 要求 \(m\) 步以内将 \(p\) 排序. 我们可以把 \(p\) 划分为若干个连续上升的自然数序列, 那么每次操作可以将若对不交的序列合并. 设这些序列从大到小排列后长度为 \(w_1,w_2,\cdots,w_k\),令 \(f(l,r,t)\) 表示用前 \(t\) 步合并完 \([l,r]\) 的最小代价, 转移有
\(\mathcal O(n^3m)\) 结束.
13.「集训队互测 2015」「UOJ #91」最大异或和 ⭐
- Link & Submission.
- 「A.数学-线性代数」
这题突破口大概是数据范围? 就算只有一次询问, 我们也得 \(\mathcal O(nm^2/\omega)\) 地插一个线性基出来, 这个已经把复杂度卡满了!
因此, 被迫排除所有数据结构的维护手段, 我们想起一个线性基上的经典题目: 静态区间 xor 最大值. 在那道题中, 通过保留存活时间最长的向量, 我们仅仅进行了 \(\mathcal O(n+q)\) 次线性基操作, 非常合适啊! 可惜, 本题的一次修改就会改变 \(\mathcal O(n)\) 个向量, 我们没有办法直接迁移过来.
记得这篇的 #8 中, 我们通过相邻向量相加的方式处理了 "选偶数个", "相邻向量相加" 而几乎不改变空间状态, 但可以几乎消除所有改动! 具体地, 我们暴力维护初始向量列 \(\{\vct a\}\) 和 \(\vct d_i=\vct a_i+\vct a_{i-1}\) (\(\vct d_1=\vct a_1\)). 不难发现两组向量的张成空间相同, 所以提供的答案应当相等.
考虑修改, 区间加变成两个单点加, 区间赋值变成了单点加和区间赋 \(\vct 0\), 我们没有必要把一堆 \(\vct 0\) 引入空间, 所以对 \(\vct 0\) 打上标记后可以均摊掉区间赋 \(\vct 0\) 的修改次数. 最终, \(\{d\}\) 只发生了 \(\mathcal O(q)\) 次有效变动, 把变动挂在时间左端点, 右端点做扫描线, 这样就转化成静态 xor 最大值了. \(\mathcal O((n+q)m^2/\omega)\).
14.「集训队互测 2015」「UOJ #93」上帝之手 ⭐
- Link & Submission.
- 「A.数据结构-线段树」「B.Tricks」
所以说啊, 抓住魔法的本质, 忘记魔法的禁忌, 敢想敢用. (?
这题看上去非常吓人, 仔细分析一下却发现前两个询问多少都在虚张声势: 第一问, \(c\in[a,b]\) 的答案显然是单调不降的, 我们可以直接确定需要询问哪个位置. 先来解决单调询问, 注意到每一天对混乱度的变化形如 \(\varphi_i:x\mapsto\min\{x+a,b\}\), 这个函数在复合作用下形式不会改变, 所以线段树维护即可.
第二问, 我们尽量不让 \(x_0\) 被取 \(\min\). 线段树二分出 \([a,b]\) 内最后一个 \(\ell_i\le x\), 从这个位置放 \(x\) 向后走即可.
第三问, 利用答案单调不降的性质, 我们只需要求出 \(c\in[a,b]\) 答案变化 (增大) 的次数. 怎么数一个不降序列的段数? 可以发现这和楼房重建维护 LGIS 的 trick 很像, 每次先递归右区间, 求答案的同时顺便求出后缀函数复合的结果, 检测左区间哪一段后缀在变化后和右区间前缀会合成一段, 递归检查即可. 这里复杂度是 \(\mathcal O((n+m\log n)\log n)\), 前两问的复杂度都是 \(\mathcal O(n+m\log n)\).
15.「集训队互测 2015」「UOJ #92」小树苗与集合 ⭐
- Link | 口胡惹, 写答辩还得看 crashed.
- 「A.数据结构-可持久化平衡树」
先考虑树上问题. 点分什么的太难啦! 我们整点直观的: 对于 \(u\) 子树, 维护两棵平衡树. 第一棵维护以到 \(u\) 距离为键值的由树上结点 (以及用来描述平衡树子树的虚点) 构成, 第二棵维护以到 \(u\) 之后还能走的距离为键值的由询问结点 (以及用来描述平衡树子树的虚点) 构成. 在树根合并的时候, 我们需要模拟两棵值域可交 treap 的合并, 这里每次拿高优先级的 treap 根 split 另一棵 treap 然后递归就行, 期望复杂度是被证明达到理论下界的.
仙人掌? 我们只需要处理环上 treap 间的交叉连边问题. 这个? 似乎? 线段树? 点和边都是 \(\mathcal O(n\log n)\)? 从 wys 论文的字里行间胡出来这个意思. 反正咱不写代码, 重点放在前面部分就行了. (