WC 历年真题分析

· · 个人记录

2021

括号路径

由于题目保证斜对称性,所以如果 x 可以走到 y,那么 y 一定可以走到 x。于是构成一种强联通性。

考虑长度为 2 的合法路径 x\rightarrow y,必然是有 (z,x,w),(z,y,w)。把这样的 (x,y) 缩成一个点,显然不会破坏其他合法解。所以不停地缩点即可,可以对边启发式合并。

表达式求值

数组每一位互不相干,所以考虑拆成 n 个单个的数来做。

首先建出表达式树。最直接的 DP 是设 f(x,i) 表示 x 节点的子树,得到结果为 i 的方案数。转移可以前缀和优化。注意到第二维离散化之后其实是 O(m) 的,因此总复杂度是 O(nm|S|)

但预处理还要除以一个 $m$ 才行。由于恰好为 $x$ 的方案数 $=$ 小于等于 $x$ 的方案数 $-$ 小于 $x$ 的方案数,因此我们预处理时可以把 DP 的 $=$ 和 $<$ 两个状态合并,从而也不需要枚举 $x$ 是啥了,直接 $2^m$ 枚举每个位置的数为 $0$ 还是 $1$,并 DP 算出表达式结果为 $0$ 的方案数即可。复杂度 $O(2^m|S|)$。询问的时候,对于 $x$,系数就是「把 $\le x$ 的数的位置上权值设为 $0$ 的 DP 值」$-$「把 $<x$ 的数的位置上权值设为 $0$ 的 DP 值」。相等的情况可以人为钦定靠前的较小。 ## [斐波那契](https://www.luogu.com.cn/problem/P7325) 列出几项就会发现 $F_n=af_{n-2}+bf_{n-1}$。 先特判掉 $a=0$ 或 $b=0$ 的情况。 于是问题转化为求 $af_{n-1}+bf_{n}\equiv 0\pmod m$ 的最小的 $n$。 假设 $b,f_{n-1}$ 存在逆元,那么有 $\frac{f_n}{f_{n-1}}\equiv -\frac{a}{b}\pmod m$。由于斐波那契在模 $m$ 意义下循环节长度不超过 $6m$,因此预处理出所有相邻两项之比值即可。 所以目标就是把问题转化到 $b,f_{n-1}$ 均和 $m$ 互质的情况。 首先同时除以 $\gcd(a,b,m)$,问题变成: $$ a'f_{n-1}+b'f_{n}\equiv 0\pmod {m'},\quad \gcd(a',b',m')=1 $$ > 结论:$\gcd(b',m')=\gcd(f_{n-1},m')

证明:

  1. 虽然 f_{n-1},f_n 在模意义下不一定互质,但由于非模意义下 \gcd(f_{n-1}, f_n)=1,可以反证出对于模意义下的 f_{n-1},f_n,有 \gcd(f_{n-1},f_n,m')=1,所以 \forall d|f_{n-1},d|m',d>1d\not|f_n,故 f|b'

因此同时除以 \gcd(f_{n-1},m')=\gcd(b',m'),问题变成:

a'f_{n-1}'+b''f_n\equiv \pmod {m''},\quad f_{n-1}'\perp m'',b''\perp m''

现在就存在逆元了,可以除过去了。

所以我们的预处理是:对于 m 的所有约数 m ',在模 m' 意义下遍历一个循环节内的斐波那契数。对于相邻两项 f_{n-1},f_n,令 d=\gcd(f_{n-1},m'),把 n 用于更新 (m',d,\frac{f_n}{\frac{f_{n-1}}{d}}\bmod {\frac{m'}{d}}) 这个状态的答案。这一部分复杂度是 O(\sum_{d|m})\approx O(m\log\log m)m=10^5 时大约为 4\times10^5)。

查询的时候首先同时除以 \gcd(a,b,m),得到 a',b',m',记 d=\gcd(b',m'),那么查询状态 (m',d,-\frac{a'}{\frac{b'}{d}}\bmod \frac{m'}{d})

用 map 实现的话常数有点大,我不开 O2 会被卡。(当然在 LOJ 的话不开 O2 也是稳过的。)

2020

有根树

参考 cmd 题解:https://www.luogu.com.cn/blog/command-block/ds-ji-lu-p6729-wc2020-you-gen-shu

注意到一个性质,就是 w 随深度递减而递增。最低的部分分一看就是每次二分答案然后在树上贪心,即把 w_i \le midi (且不是根)全部排出在外,剩下的点就是一个包含根的连通块,检查一下点数是否 \le lim 即可。

然后我们发现这样找到的最优解一定可以满足 \min_{i\in C}\{w_i\}\ge |C|\ge \max_{i\not\in C}\{w_i\},并且此时 ans=|C|

考虑不满足的时候如何调整。

  1. \min_{i\in C}\{w_i\}<\max_{i\not\in C}\{w_i\},把 C 中最小的和不在 C 中最大的交换,|C| 不变,而 \max 不变大,不会变劣。

  2. \min_{i\in C}\{w_i\}<|C|,由上一条可知当前答案瓶颈在于 |C|,于是把 C 中最小的踢出去,答案不会变劣。

  3. \max_{i\not\in C}\{w_i\}>|C|,则答案瓶颈在于 \max,那么把不在 C 中的最大的加入 C 中,答案不会变劣。

所以思路就是,不断调整使得满足上式,并且让 ans=|C| 尽量小。

综上,每次只需要 O(1) 次调整。

实现方面,用树剖 + 线段树,线段树每个节点维护两个值 mn,mx,分别表示 \min_{i\in C}\{w_i\}\max_{i\not\in C}\{w_i\},特殊地,当不存在时分别 +\inf,-\inf。加入一个点的时候先默认不在 C 中(为了 ans=|C| 尽量小),之后调整。并且调整 1 其实可以由调整 2,3 得到,因为 2,3 调整完后有 mn\ge |C|\ge mx

时间复杂度 O(m\log^2n),看得出出题人专门卡这个复杂度,但其实可以通过。

2019

远古计算机

提交答案题

Sub1

node 1
read 0 a
write a 0

Sub2

预处理出前 \log 左右项斐波那契数,然后把 jmp val 当作 switch 用。

Sub3

显然存在最优方案是单线路传输。

所以找到最短路即可。

Sub4

乱搞点。

第一感觉是搞出最短路图,然后沿着最短路图运输,先到的先转运。可能 random_shuffle 一下。

实际上只有不打乱并且前向星存图,才能恰好找到最优解。(偷懒用 vector 存图就吃大亏了)。

Sub5

不能乱搞了。

首先枚举 1-10 出发顺序的排列。

然后设 book(x,i) 表示 x 点在 i 时刻是否已占用。最短路转移时只能转移到 book(x,i),book(x,i+1) 都为 0 的时刻。

会发现最优解轮数是 21

所以只要当前排列的结果是 21 了,就输出方案并结束。

提交答案题求最优解,往往会用到多次枚举或者随机化

2018

通道

求点对使得在三棵树上的路径长度和最大的这个最大值。

对第一课树边分治(顺便一提,边分治重构后的树点数为 4n)。当前分治子问题中的点被分为两部分 L,R。在第二棵树上对当前子问题涉及到的点建虚树,然后从根 dfs,假设当前在 p,我们考虑计算虚树上以 p 为 LCA 的点对的贡献。假设他们是 x,y,并且 x,y 分别属于 L,R,那么贡献即为

d_1(x)+d_1(y)+d_2(x)+d_2(y)-2d_2(p)+dist_3(x,y)

其中 d_1 表示在第一棵树上到当前分治中心的距离,d_2 表示在第二棵树上的深度,dist_3 表示在第三棵树上的距离。

$$ [d_1(x)+d_2(x)]+[d_1(y)+d_2(y)]+dist_3(x,y) $$ 考虑在第三棵树上,对每个点 $x$,新建点 $x'$,且与 $x$ 连边权为 $d_1(x)+d_2(x)$ 的边。容易看出上述式子变成了在新的第三课树上求**直径**。 接着考虑在第二棵树上 DP。设 $f(x,0/1)$ 表示考虑 $x$ 节点的子树,考虑子树内在 $L/R$ 中的节点,在第三棵树上构成直径的点对。合并时,新的直径端点显然从旧直径端点中产生,所以可以转移。转移前顺便计算一下答案,类似于 DP 找直径的过程。 实现时,不需要在第三棵树上新建节点,等效地,由于计算距离公式是 $d(x)+d(y)-2d(\operatorname{LCA}(x,y))$,并且我们需要计算的一定有 $x\neq y$,所以可以令 $w(x)=d(x)+\text{额外权值}$,然后把计算公式变成 $w(x)+w(y)-2d(\operatorname{LCA}(x,y))$ 即可。 ## [州区划分](https://www.luogu.com.cn/problem/P4221) 一个子集合法当且仅当不连通或存在奇度数的点。对于不合法的集合可以直接设其权值为 $0$。 设 $f(S)$ 表示 $S$ 中的点已经完成划分的权值和。 $$ f(S)=\sum_{x+y=S}f(x)\frac{w(y)}{w(S)} $$ 这是子集卷积的形式,考虑用 FWT 优化。但貌似是自己卷自己? 写成子集卷积的形式: $$ w(S)f(|S|,S)=\sum_{x+y=S}f(|x|, x)w(|y|,y) $$ 可以看做 $f(|S|)$ 由 $f(|x|)$ 卷 $w(|y|)$ 得到,$|x|<|S|$,所以不会自己卷自己。所以按集合大小升序进行计算即可。每一轮最开始要先 IFWT 回去,点乘 $w(S)$,再 FWT 回来。 就是常数有点大,不开 O2 过不去。 ## [即时战略](https://loj.ac/p/2341) ### 链 此部分限制为 $n+\log n$。 直接看成序列问题。 已知部分一定是一个区间,维护左右端点 $L,R$。一个暴力的想法是,询问 $(L,x)$,如果未知说明 $x$ 在 $L$ 左边,那么一直询问直到找到 $x$。$R$ 同理。这样步数是 $2n$。 做题的时候没办法就 `random_shuffle` 了一下,没想到就过了。 随机后是期望 $n+\log n$ 的。因为每次开始期望拓展出一半未知的点,于是总共 $\log n$ 轮,而每次至多失败一次,所以总尝试数大约为 $n+\log n$。实际效果更佳。 ### 其它 此部分限制为 $n\log n$。 由完全二叉树的 subtask 得到启发,如果我们能让树高为 $\log n$,就大有可为。 于是想到了点分树。假设我们维护出了已知节点的点分树(在原树上一定是一个连通块),然后现在要找到新节点 $x$。我们可以先询问 $(rt,x)$,假设结果是 $k$,那么 $k$ 不断跳点分父亲直到根的儿子,我们就知道了 $x$ 在点分树的哪颗子树中,递归下去,直到某一次 $k$ 是未知节点。由于点分树高 $\log n$,于是我们可以花 $\log n$ 次询问,在 $O(\log^2 n)$ 的时间复杂度内,找到未知链的链顶。接下来,暴力拓展出这一条新链,并暂时直接接到点分树(当前子树的根)上。 但这样点分树就可能不平衡了,考虑替罪羊重构。即,设一个参数 $\alpha=0.7$,然后从当前节点一直跳到根,找到最靠上面的满足 $\alpha size_{son}>size_x$ 的节点,然后把 $x$ 的子树拎出来点分重构。需要注意如果 $x$ 是点分根的话,重构后根可能改变,需要判断一下。由替罪羊的那一套理论,均摊是 $\log n$ 的。**这就是所谓动态点分树**。 最开始 `random_shuffle` 一下更佳。