Solution Set - “盛开无法定格的花”
- 0.「ARC 160D」Mahjong
- 1.「BJOI 2017」「洛谷 P3715」魔法咒语
- 2.「清华集训 2017」「洛谷 P4227」我的生命已如风中残烛
- 3.「集训队互测 2018」「洛谷 P9249」完美的旅行 ⭐
- 4.「NOI Simu.」万家灯火
- 5.「NOI Simu.」逃亡
- 6.「NOI Simu.」地铁 ⭐
- 7.「BJOI 2018」「洛谷 P4458」链上二次求和
- 8.「THUSCH 2017」「洛谷 P7451」杜老师
- 9.「THUSCH 2017」「洛谷 P7454」如果奇迹有颜色 ⭐
- 10.「UR #4」「UOJ #53」追击圣诞老人
- 11.「CF 1718E」Impressionism ⭐
- 12.「UR #5」「UOJ #60」怎样提高智商
- 13.「UR #5」「UOJ #61」怎样更有力气
- 14.「UR #5」「UOJ #62」怎样跑得更快 ⭐
- 15.「THUSCH 2017」「洛谷 P7450」巧克力
0.「ARC 160D」Mahjong
- Link & Submission.
其实思路比较套路化啦. 先看看如何判定确定的 \(\{a_n\}\) 合法. 注意到 \(a_1\) 一定被横向操作 \(a_1\bmod k\) 次, 纵向操作 \(a_1/k\) 次, 不妨令 \(p_1=a_1\bmod k\), \(q_1=a_1/k\). 那么 \(a_2\) 一定被横向操作 \(p_2=(a_2-p_1)\bmod k\) 次, 以此类推, 最终如果我们可以得到非负整数序列对 \((\{p_{n-k+1}\},\{q_n\})\), 则 \(\{a_n\}\) 合法.
反过来, 操作过程被 \(\{a_n\}\) 唯一确定, 那么 \(\{a_n\}\) 被操作过程唯一确定, 我们只需要计数合法的 \((\{p_{n-k+1}\},\{q_n\})\). 引入 GF 嘛, 显然有
通分之后枚举分子系数即可. 暴力算组合数, \(\mathcal O(n^2)\).
1.「BJOI 2017」「洛谷 P3715」魔法咒语
- Link & Submission.
纯纯的 AC 自动机上 DP. \(L\) 小基本串长的时候直接 DP, \(L\) 大基本串短的时候矩阵快速幂, 复杂度自己算算喵.
2.「清华集训 2017」「洛谷 P4227」我的生命已如风中残烛
- Link & Submission.
- 「A.计算几何」
一道好写但难调的怪东西捏.
除了第一次接触钉子外, 我们接触钉子的角度是很有限的 — 最多 \(\mathcal O(n^2)\) 种可能的接触. 同时, 可以相信想象到, 圆心变化由若干个 \(\rho\) 形周期拼接而成, 除开转圈圈的部分是 \(\mathcal O(n)\) 的, 所以我们只需要模拟这个过程就行.
对于每个钉子, 预处理以它为原点, 其余点的极角, 极角排序后环上的距离及其区间最小值的倍增数组, \(\mathcal O(n^2\log n)\). 尝试从某个钉子转圈碰其余钉子时, 先二分出极角位置, 再倍增第一个撞到的钉子, 单次 \(\mathcal O(\log n)\). 最终复杂度 \(\mathcal O(T(n^2\log n+mn\log n))\). 需要注意 \(\epsilon\) 的限制, long double
, 转圈条件 (长度可行, 初始进入角还得相同) 等等一系列细节.
3.「集训队互测 2018」「洛谷 P9249」完美的旅行 ⭐
- Link & Submission.
- 「A.数学-FWT」「A.数学-线性代数」
我们可以 \(\mathcal O(mn^3)\) 地求出一次 \(i\in[1,m]\) 步的旅行, 权值为 \(j\in[0,n)\) 的超集方案数 \(g(i,j)\). 注意到当 \(j\) 固定时, \(g(i,j)\) 是 \(A^i\) 中固定的某些位置的线性组合, 因而此时 \(g(\cdot,j)\) 存在 \(\mathcal O(n)\) 阶线性递推关系, 进而希望求的答案 \(f(\cdot,j)\) 也存在 \(\mathcal O(n)\) 阶线性递推关系, BM 暴力求出来递推即可. 复杂度 \(\mathcal O(n^4+n^2m)\).
- Berlekamp-Massey 算法
对于数列 \(\{a_0,a_1,\cdots,a_{n-1}\}\), 定义数列 \(\{r_0=1,r_1,r_2,\cdots,r_m\}\) 为其递推式, 当且仅当
称这里的 \(m\) 为递推的阶数. BM 算法用于找到 \(\{a_{0..n-1}\}\) 的阶数最小的递推式.
方便起见, 这里令 \(f_i=-r_{i+1}\), 我们要求 \(a_i=\sum_{j=0}^{m-1}a_{i-j-1}f_j\). BM 算法会增量地求解 \(\{a_{0..n-1}\}\) 每个前缀的递推式, 这里, 记前缀 \(i\) 的递推式为 \(F_i=\{f_{i,0},f_{i,1},\cdots\}\).
初始时, \(F_0=\{\}\). 考虑 \(a_i\) 加入序列时:
-
\(\{a_{0..i}\}\) 满足 \(F_{i-1}\), 则 \(F_i\gets F_{i-1}\).
-
否则, 若 \(a_i\) 是序列第一项非 \(0\) 位置, 令 \(F_i=\{0\}^i\), 这里的上指标表示序列拼接次数.
-
再否则, 令 \(\Delta_i=a_i-\sum_{j=0}^{m-1}a_{i-j-1}f_{i-1,j}\). 我们尝试找出一个修正序列 \(G\), 使得
\[\sum_{j=0}^{m'-1}a_{k-j-1}g_{j}= \begin{cases} 0, & k\in[m',i)\\ \Delta_i, &k=i \end{cases}. \]这样令 \(F_i\gets F_{i-1}+G\) 就满足条件了. 设上次修正需要修正的位置是 \(F_w\), BM 给出的构造是
\[G=\{0\}^{i-w-1}\left\{\frac{\Delta_i}{\Delta_w}\right\}\left(-\frac{\Delta_i}{\Delta_w}F_w\right). \]注意 \(F_w\) 是 \(w\) 位置上的递推式被修正前的值. 嗯, 挺暴力的, 强行把 \(w\) 上 \(\Delta_w\) 的偏差带上系数搬到 \(i\) 位置来抵消 \(\Delta_i\) 就行了.
暴力实现, \(\mathcal O(n^2)\). 此外, 比较有意义的结论是: 已知存在 \(m\) 阶递推的数列, 输入前 \(2m\) 项就能得到正确的递推.
4.「NOI Simu.」万家灯火
- Private link & Submission.
- 「A.树论-点分治/点分树」
点减边? 边的要素比较多, 我们不如就数有多少个被点亮的点的连通块 LCA, 在 LCA 的父亲处贡献答案, 这样修改的信息是 \(\mathcal O(1)\) 的. 写个 \(\mathcal O(n\log^2n)\) 点分树就行了. 有个睿智写的 \(\mathcal O(n\sqrt n)\) 树分块被卡常.
5.「NOI Simu.」逃亡
- Private link & Submission.
- 「A.数学-组合计数」
考虑一个点怎么算, 很自然的思路是考察 "从 \(0\) 出发走 \(n\) 不, 从未到达 \(x=k\)" 这一事件的概率, 枚举行走的终点 \(x=d\), 反射容斥一下可以得到
发现随着 \(k\) 的移动, 概率变化量可以 \(\mathcal O(1)\) 计算. 预处理出 \(p_k=\Pr[\dots~x=k]\), 枚举所有可能被到达的位置, 计算其同时不被 \(m\) 个起点到达的概率, 继而得到被至少一个点到达的概率, 加起来就是期望. 复杂度 \(\mathcal O(nm^2)\).
6.「NOI Simu.」地铁 ⭐
- Private link & Submission.
- 「A.图论-网络流-最大流/最小割」「B.Tricks」
虽然很想吐槽放一道纯 trick 题在这里是什么想法, 但 NOI 好像也干过这事儿, 咱还是忍气吞声. (
给出的图是平面图, 我们早已熟悉 "平面图最小割 \(\to\) 对偶图最短路" 的 trick, 这里提示我们可以反过来用 "平面图最短路 \(\to\) 对偶图最小割" 转化题目.
转化到最小割的优势在于: 在流网络中, 我们能更加轻松地描述 "两个东西同时选或不选". 具体来说, 原图的某个点被最短路经过, 等价于以它为右端点的一条边被经过, 也等价于以它为边界的最大面在对偶图中对应的点在 \(T\) 集. 那么限制条件就是 "\(x\in T\Leftrightarrow y\in T\)", 加条双向边就好. 复杂度 \(\mathcal O(\text{Dinic}(n,m+k))\).
完了?
虽然没想到简单的构造, 但确实可能存在割不是路径的情况. 不过, 如果割不是路径, 那么从 \(S\) 到 \(T\) 一定存在一条路径, 这条路径包含至少两条割边. 此时, 路径上就会呈现 \(S\to T\to S\to\cdots\) 的切换. 我们只需要对所有原图边的对偶边增加一条不可割的反向边, 就能限制 \(T\to S\) 的出现, 进而排除掉这种情况.
正确性方面, 虽然新增的限制可能让对偶图非平面, 但是新增的不可割反向边限制了所有可行割都可以对偶回路径, 那么求出的最小割也能对偶回路径, 所以这个算法的确是正确的.
7.「BJOI 2018」「洛谷 P4458」链上二次求和
- Link & Submission.
- 「A.数据结构-线段树」
A totally dirty work. 注意修改和询问都可以差分降降自由度. 对于询问 "区间长度 \(\le k\) 的区间和之和", 一个想法是维护以每个点为右端点的答案. 记 \(s_i=\sum_{j=1}^ia_j\), \(t_i=\sum_{j=1}^ija_j\), 那么此时答案为
维护 \(s,t\) 的区间和即可. 所有维护过程可以拆分为对 \(\sum c_ix_i\) (\(c\) 为常序列) 的区间加区间和维护, 开很多很多棵简单线段树比较好写, 就是比较慢. 复杂度 \(\mathcal O(n+m\log n)\).
8.「THUSCH 2017」「洛谷 P7451」杜老师
- Link & Submission.
- 「A.数学-线性代数」
令 \(a=\prod_ip_i^{\alpha_i}\), 那么在 \(\varphi:a\mapsto\begin{bmatrix}\alpha_1\bmod2&\alpha_2\bmod2&\cdots\end{bmatrix}^T\) 的意义下, 一个数 \(a\) 完全平方数等价于 \(a\mapsto\vct{0}\), 我们要干的事情就是在 \(\{\varphi(i)\mid i\in[L,R]\}\) 中找一些向量加出 \(\vct{0}\).
另一个基础的转化, 对于 \(p>\sqrt V\), 这些数至多在一个数中出现一次. 设 \(S_p=\{kp\mid kp\le V\}\), 那么我们在 \(S_p\) 中选出的向量和也应该为 \(\vct0\), 也即是只能选出偶数个元素. 这很简单, 令 \(\mathcal S_p=\{\varphi((k-1)p)+\varphi(kp)\mid p<kp\le V\}\) 就行. 容易验证所有偶数个向量的线性组合仍然能选出来. 加上区间限制也是类似的.
最后, 我们要做的问题就是求出 \([L,R]\) 内所有元素对应向量的张成空间, 设有效向量个数为 \(c\) (因为对 \(p>\sqrt n\) 的处理, 大素数倍数第一次出现的位置不能贡献向量, 这里有 \(c\le R-L+1\)), 空间秩为 \(k\), 则答案为 \(2^{c-k}\). 打表发现大约 \(5\times10^3\) 个连续整数对应的向量就已经让空间满秩, 此后不需要再插入线性基, 直接跑就能过了, 复杂度不会算喵.
(不过, 我们需要预处理 \(10^7\) 个 \(\pi(\lfloor\sqrt V\rfloor)\) 维向量, 加上 std::bitset
空间也吃不消, 所以只能保留前 \(10^7/2\) 个, 剩下一半在线计算.)
9.「THUSCH 2017」「洛谷 P7454」如果奇迹有颜色 ⭐
- Link & Submission (含打表过程).
- 「A.数学-Pólya 计数」「A.数学-多项式」「B.Tricks」
算是不难但 educational 的题. 首先肯定 Burnside 转换掉旋转同构, 现在我们需要对一个环的方案计数.
这个环的确没办法破, 我们先给出一个优秀一点的暴力. 令 \(f_k(\ell,S)\) 表示钦定序列的第 \(i\in[1,k)\) 个位置颜色为 \(i\), 第 \(i\) 个位置颜色为 \(\in[1,k)\) 时, 填完长度为 \(\ell\) 的序列, 最后 \(m-1\) 个颜色的状态为 \(S\) 的方案数. 直接递推的复杂度大概 \(\mathcal O(nm^m)\), 矩阵快速幂也得 \(\mathcal O(m^{3(m-1)}\log n)\).
然后就是比较 tricky 的点, 不难发现答案之间存在一个递推关系, 我们暴力打表一段前缀后用 Berlekamp-Massey 求出递推式即可. \(m=7\) 时递推式长度在 \(L=400\) 左右. 此后求答案时用 Bostan-Mori 算分式远项就行. 复杂度 \(\mathcal O(d(n)L^2\log n)\), 最后一个点也可以直接跑过.
10.「UR #4」「UOJ #53」追击圣诞老人
- Link & Submission.
好像没什么合适的 tag, 就是一个贪心扩展求前 \(k\) 大的东西. 所有状态可以表示成 \((w,s,t)\), 表示下一步需要在 \(s\to t\) 的路径上选点, 选出权值最小的点时, 权值和为 \(w\). 每次去掉最小点会扩展出最多两个状态, 走到最小点会扩展出最多三个状态. 因此复杂度 \(\mathcal O((n+k)\log n)\).
值得注意的是卡空间技巧:
std::vector
存图空间大于前向星.- 静态数组 +
std::make/push/pop_heap
维护堆空间小于std::priority_queue
.
11.「CF 1718E」Impressionism ⭐
- Link & Submission.
- 「A.构造」
要敢猜, 猜猜 need!
若 \(A\) 的第 \(i\) 行和 \(B\) 的第 \(j\) 行可以匹配, 一个必要条件是这两行非 \(0\) 元素组成的可重集合相同. 而想要让这两个可重集合相同, 又需要 (它们所在列上的非 \(0\) 元素组成的可重集合) 组成的可重集合相同… 如此套娃, \(\min\{n,m\}\) 次迭代后等价类的划分就稳定了. 此时将同一个等价类上的行任意匹配, 行匹配完成后就按照列的长相匹配即可. 复杂度 \(\mathcal O(nm\sqrt{nm})\), 沉默是今晚的正确性证明. (
12.「UR #5」「UOJ #60」怎样提高智商
- Link & Submission.
哈哈哈哈哈哈哈哈哈哈哈如何提高智商. 欸, 如何提高智商?
A 0 0 0 0
A 0 0 0 0
...
A 0 0 0 0
方案数是 \(4\times3^{n-1}\). 如果没有四选题,方案数肯定小于这个值, 而四选题最多出现一道, 否则两道四选题中间的题方案贡献会极少.
13.「UR #5」「UOJ #61」怎样更有力气
- Link & Submission.
Kruskal, 其实我们只需要精准地枚举所有不会与已有森林成环的边就能保证复杂度了. 整个树剖啊线段树啊什么东西都行. 写得丑是 \(\mathcal O(n\log^2n)\) 的 (\(n,m,p\) 同阶, 下同).
更优雅的做法是, 我们只需要讨论单独的 \(\mathcal O(p)\) 个点, 其他点在一个团被枚举出来的时候肯定一下子就连通了. 这样可以做到 \(\mathcal O(n\log n)\).
14.「UR #5」「UOJ #62」怎样跑得更快 ⭐
- Link & Submission.
- 「A.数学-线性代数」「A.构造」「B.Tricks」
欲解
不妨设系数矩阵为 \(A\). 第一个观察是 \(\gcd\) 和 \(\lcm\) 可以合并成一个东西, 减少对 "约数倍数" 的涉及. 即, \(A_{ij}=\gcd(i,j)^c\lcm(i,j)^d=(ij)^t\gcd(i,j)^s\).
\(\gcd(i,j)\)? 很难不让人联想到 Smith 矩阵. 我们先回忆一下一道有意思的题目:
Q: 设 \(M_{ij}=\gcd(i,j)\), 求 \(\det M\).
A: 构造 \(A_{ij}=[i\mid j]\), \(B_{ij}=[i=j]\varphi(i)\), 则
\[\begin{aligned} (A^TBA)_{ij} &= \sum_{k=1}^n[k\mid i]\cdot \varphi(k)\cdot [k\mid j]\\ &= \sum_{k\mid\gcd(i,j)}\varphi(k)\\ &= \gcd(i,j)\\ &= M_{ij}. \end{aligned} \]因此 \(\det M=\det A^{-1}\det B\det A=\prod_{i=1}^n\varphi(i)\).
记得当时在老吴的车上, Tiw 和 Charlotte 围着 Rainybunny 讲这题. (
这种构造把 \(\gcd(i,j)\) 转化成 \(\sum_{k\mid\gcd(i,j)}\varphi(k)\). 本质上, 只要矩阵上的 \(f(i,j)\) 能够构造出\(f(i,j)=\sum_{k\mid\gcd(i,j)}g(k)\) 的关系, 我们都能用这个 trick 拆开矩阵. 另一方面, 先假设 \(A\) 可逆, 那么我们就是要求出 \(\vct{x}=A^{-1}\vct{b}\). 可以猜测, 我们需要构造出三个矩阵拆分 \(A\), 同时三个矩阵各自的线性变换都能快速作用在向量上, 这样问题就解决了.
观察上例中的第一行, 我们一步步将它扩展.
对于 \(\varphi(k)\) 这个位置, 我们需要替换成 \(f(k)\), 满足 \((f\star I)(n)=\text{id}(n)^k\), 这个 \(f\) 可以暴力 \(\mathcal O(n\log n)\) 递推出来. 令 \(Y_{ij}=[i=j]f(i)\).
对于 \([k\mid i]\) 这个位置, 它可以顺便带上 \(i^t\) 的贡献. 令 \(X_{ij}=[j\mid i]i^t\).
对于 \([k\mid j]\) 这个位置, 同理, 顺便带上 \(j^t\) 的贡献. 令 \(Z_{ij}=[i\mid j]j^t\).
到此,
应该要赢了, 来求逆 (不会解可以选择打表, 规律很显然):
好的, \(X^{-1},Z^{-1}\) 作用在向量上也是一个可以 \(\mathcal O(n\log n)\) 算的东西. 本题差不多就结束了. 最后. 如果 \(A\) 不可逆, 那么必然是 \(Y\) 不可逆, 此时在 \(X^{-1}\) 作用下所有 \(Y_{ii}=0\) 的 \(b_i\) 应当已经被消为 \(0\), 否则无解. 如果有解, 就假装没看见这个 \(Y_{ii}=0\) 正常运算即可, 这样就能找到一种自由元的赋值. 复杂度 \(\mathcal O(qn\log n)\).
15.「THUSCH 2017」「洛谷 P7450」巧克力
- Link & Submission.
- 「A.随机化」「A.DP-状压/插头 DP」
想要的颜色数 \(k\) 很小, 提供的颜色却很多很多, 这是一个常见的随机化 trick: 将提供的颜色随机映到 \([0:k-1]\) 上, 对这个目标颜色集合跑一个类似斯坦纳树算法的状压 DP 就能求出最小的满足要求的带权连通块.
利用这个子问题, 将所有点的点权赋为 \(1\), 即得第一问答案 \(c\); 二分中位数 \(x\), 将点权赋为 \(B+[a_u>x]\) (其中 \(B\) 是一个足够大的数), 判断最小权是否 \(\le cB+(c/2)\) 即可. 设随机映射测试次数为 \(t\), 则复杂度 \(\mathcal O(Ttnm3^k\log nm)\), 错误率不超过 \((1-k!/k^k)^t\). 取 \(t=200\) 或者少一点都行.