24oi & wgsz 集训
Day 2 8.18
T1
推式题.推式能力不强,消耗了大量时间.
由期望的线性,可以对每个位置分开计算贡献.
每个位置的地位对等.
对于每个位置,考虑进行 \(m\) 次操作后仍在该位置的信封仍在原位置的概率.考虑递推 \(F_i\) 表示 \(i\) 操作后仍在原位置的概率.
可以求出通项.也可以矩阵快速幂求解.
T2
简单树形 DP.
T3
抽象成树模型的题目.当然也可以用字符串 lcp 来理解吧.
考虑已知最终基因 \((A,B)\),还原到 \((gcd(A,B),gcd(A,B))\) 序列是固定的.这是一个辗转相减的过程:若 \(A>B\) 则上一个操作一定是 \(A+B\rightarrow A\).否则若 \(B>A\) 则上一个操作一定是 \(A+B\rightarrow B\).对于 \(n\) 个基因和 \(q\) 个可能的祖先基因都构建出进化序列:若 \(A\) 加,则这一步为 \(0\),否则这一步为 \(1\).则问题变为了:对于 \(q\) 个子串查询 \(n\) 个子串中存在多少个是其前缀.
-
压缩 trie.老师上课提到了,但其实复杂度是错误的.
-
字典排序法.
考虑一个串 \(S\) 在 \(n\) 个串中作为前缀出现的次数,可以将 \(S+c_{mn}\) 和 \(S+c_{mx}\) 放入 \(n\) 个串中排序.其 \(rk\) 的差值就是中间串的个数.
T4
这种东西真的有用吗?
\(5\) 维偏序下 ploy log 的解法已经没有优势了.KD-Tree 的常数又是极大的.std 给出的是一个较轻量的 \(n^2/\log\) 做法.
将序列按 \(k=\log n\) 分块.对于每个块预处理 \(2^k\) 中比较情况的答案.这部分的复杂度是 \(O(2^kn/k)\).转移时,块内暴力转移;快外整块整块得转移,我们需要快速知道一个数在这个块内的比较结果,然后我们就可以使用预处理的结果进行转移了.对于每个块,按 \(5\) 维各排一次序,预处理出集合(bitset) \(S_{d,i}\) 表示块内 \(d\) 维前 \(i\) 小的数的集合.求解时对于 \(5\) 维各做一次二分,然后将得到的集合与起来即可.这部分做一次的复杂度是 \(O(\log k)\)(请注意,这里算集合并集的复杂度忽略,其应为 \(O(k/w)\),在 \(k\) 取到 \(\log n\) 时为 \(O(1)\)).求解一个点需要的时间是 \(O((n/k)\log k)\).那么查询的总复杂度为 \(O((n^2/k)\log k)\).
将 \(k\) 代换成 \(\log n\) 感受一下全局的时间复杂度:
Day 3 8.19
Day 3 大失败
T1
非常简单的 DP.
T2
惨剧从 P9120 [春季测试 2023] 密码锁中那个 更具有拓展性的做法 开始.
问题本质上在求最小的值域区间长度 \(\lambda\) 使得存在一个长度为 \(\lambda\) 的区间 \([pos, pos+\lambda]\) 中包含 \(n\) 中不同种类的点.
一种比较顺应思路的做法是值域上双指针.考虑存在一种最优情况的区间端点位于某个节点上,那么我们检查每个节点作为右端点包含 \(n\) 类节点的最短区间即可.
更具有拓展性的做法,本质上是一种数据结构反演.考虑在左端点上判定答案.考虑一个左端点 \(l\),存在一个节点位置 \(pos\) 使得使得 \(pos\in [l,l+\lambda]\).那么 \(l\in[pos-\lambda, pos]\).那么问题变成:是否存在一个左端点被 \(n\) 类区间覆盖.
到此为止两种做法似乎并没有什么区别.第二种似乎还多了一个 \(\log\)——在这一道题上会惨死.
那么其拓展性体现在哪里呢:其在高维情况下较为优秀.如果问题变成:在二维平面上存在 \(n\) 类点,现在每类中选择一个点出来最小化距离极差(定义 \((x_1,y_1),(x_2,y_2)\) 的距离为 \(\max(|x_1-x_2|,|y_1-y_2|)\)).那么从找到合法区间的方向思考问题就不是那么好做了.而考虑数据结构反演则变成了考虑若干矩形面积并的重叠是否存在重叠 \(n\) 次的地方.这个是容易实现的.
T3
DP.我没有完全理解.
T4
搜索.可以流.考虑每个点需要被覆盖,可以转化为上下界网络流.
Day 4 8.20
T1
现在当前的决策只需要已知手上盘子的状态.设计 \(F_{i,j\in 0\sim 4, k\in 0\sim 4}\) 表示考虑前 \(i\) 个盘子操作完后,手上的两个盘子状态为 \(j,k\) 的至多得分.可以通过一些转移技巧使其转移次数变成 \(O(状态数)\).
T2
按题意模拟即可.
T3
很有趣的题目.首先我们需要认同一个观点:\(+1\) 的操作只可能是某些位置的 DP 值 \(+1\),\(-1\) 类似.这启发我们:对于一个操作,只需要知道所有需要修改的位置,即可以做到快速修改.
不失一般性地考察 \(+1\) 操作的影响范围:
-
其影响的范围是一个联通块.这是容易知道的.
-
影响范围中不存在两种类型的折角.为了方便叙述设影响集合为 \(S\).
- \((x,y),(x+1,y),(x,y+1)\in S,(x+1,y+1)\notin S\).
- \((x,y),(x-1,y),(x,y-1)\in S,(x-1,y-1)\notin S\).
这是容易证明的.
考虑 \(+1\) 的点为 \((sx,sy)\).先处理出 \(sx\) 这一行的扩展范围,可以知道这个范围是一个连续区间,不然会违反约束 2.接下来每行也都是区间,并且存在性质:左端点和右端点均单调不减.寻找所有端点后每一行使用 Bit 进行更新.那么单次 \(+1\) 时间复杂度是 \(O(n\log n)\).
T4
点分治.对于一条路径 \((num_a, num_b, num_c)\) 保留信息 \((num_b-num_a, num_c-num_b)\) 即可.在这题里 unordered_map 慢于 map.
Day 5 8.21
Day 6 8.22
T1
按题意模拟即可。
T2
博弈论问题。
考虑过程发生在 \(X,Y\) 的简单路径上:当 \(X\) 或 \(Y\) 中的一个点偏移出 \(X,Y\) 的简单路径后,两个点的博弈过程变成分立的。那我们考虑模拟这个脱离的过程。
设路径为 \(p_1(X),p_2,p_3,\cdots,p_k(Y)\)。设 \(X\) 的位置为 \(a\),\(Y\) 的位置为 \(b\)。初始时 \(a=p_1\),\(b=p_2\)。
递归进行如下操作:
- \(X\) 从 \(a\) 脱离可以获胜,那么存在 \(X\) 必胜策略。
- \(Y\) 从 \(b\) 脱离可以获胜,那么不存在 \(X\) 的必胜策略。
- 如果上两者均不满足,那么重复这个过程。
\(ST-Table\) 预处理区间最小值辅助判断,总复杂度为 \(O(n\log n)\)。
T3
按题意模拟即可。
T4
~~ 观察到 \(O(m^2)\) 可过。~~
介绍一种 \(O(nK\log n)\) 的优秀做法.
考虑 \(F_{i,j,K}\) 表示如果在 \(a_{0,i}\) 处 \(+1\),则会在 \(a_{K,i}\) 处 \(+F_{i,j,K}\).其实其只跟 \(j-i,K\) 有关.
对固定的指标 \(i\),考虑 \(j\rightarrow i\) 的贡献,有结论:
证明:
- 高维前缀和递推式形式是斜杨辉三角.
- 可以通过网格图路径计数理解递推式.
想办法将系数中和 \(i\) 相关的部分分离出来.
于是我们对于每个 \(l\) 维护一个树状数组,这题就做完了.
Day 7 8.23
做题记录
T1
栈优化 DP.
T2
取模存在性质:
如果 \(p\equiv q(\bmod\ (AB))\),那么 \(p\equiv q(\bmod\ A)\)。
考虑题目里同余方程的形式:
发现可以在模数上做手脚:
那么考虑费马小定理有:
那么变成了普通的 CRT 形式。考虑和原问题的关系:
这个转换是向松的方向转化。而 CRT 可以求出 \(\bmod\ n\) 下的唯一解。故我们只需要判定这个解是否合法即可。可以暴力检查即可。
T3
构造出的最优解肯定形如这样:耗费若干代价在几个位置放上棋子,然后剩下的连续空段可以不消耗代价填上。
考虑什么样的连续空段是可以 \(0\) 消耗可以填满的。从长度为 \(1\) 的段反推,序列满足这样的递推式:
存在通项 \(F_{i}=2^i-1\)。
那么对于原棋盘的每一个空段贪心填补即可。
小结
-
思考适当结合所学算法。例如今天的 \(T2\) 在形式上类同 CRT。应该尝试思考如何在形式上转化使得原问题的形式像 CRT 靠近。
-
检查特殊情况。最好在考场写代码的时候就将思考到的特殊情况记录下来。写完容易忘。一定要检查!
-
开场看完所有题目的题面。
Day 8 8.24
首先这一场的 T1,T3 是两道非常简单的题目。
T2
容易想到一个状态数为 \(n^2\) 的 DP 做法。设 \(F_{i,j}\) 表示考虑前 \(i\) 个数,最后一个数被改成 \(j\),前 \(i\) 个数改成不降的最小代价。状态数 \(n^2\) 的保证是:存在一种最优方案,使得每一个位置最终的数都是原来的 \(n\) 个数之一(考虑任取一种最优解,然后进行调整)。
考场做法(在树上为错解,但序列上是正确的)。
考虑对于原始序列 \(a\),\(a\) 升序排序后为 \(b\),贪心构造目标序列 \(c\),最小化 \(\sum |a_i-c_i|\)。由上述的结论有存在一种 \(c\) 的构造形如若干个 \(b_1\) 后接若干个 \(b_2\) \(\cdots\) 后接若干个 \(b_n\)。那么先将 \(c[1,n]\) 抬升到 \(b_1\)。考虑抬升到 \(b_2\) 的区间一定是抬升到 \(b_1\) 的一个后缀。其后依次类推。
对于 \(i\) 位置考虑后缀 \(a[i:]\),最大能使其更优的抬升量为 \(p\)。考虑如何确定 \(p\)。有如果存在 \(a[i:]\) 的前缀 \(a[i:j]\) 满足其中 \(< p\) 的个数 \(>\) \(a[i:j]\) 中 \(>p\) 的个数,那么 \(i\) 抬升到 \(p\) 是不优的。
发现在上述的这种情况中,将 \(a[i:j]\) 的抬升量向下调整 \(-1\) 更优。这样对于 \(a[j+1:]\) 的限制更松:整体答案不会更劣。故得证。
那么有 \(c_i=max_{j\le i}p_j\)。故快速确定 \(p_i\) 即可。这个是可做的。无法扩展到树上。
正解
颠覆常识的 DP 优化。其基于最开始说的 \(n^2\) 的 DP。事实上,其不依赖值域取到 \(n\) 个数的值域:也就是说,其基于的是暴力 \(O(nV)\) 的 DP。
沿用 \(F_{i,j}\),\(j\) 取遍整个值域。考虑数组 \(G_i\),\(G_{i,j}\) 表示 \(\min_{k\le i}F_{i,k}\)。
结论:\(G_i\) 是一个 \(|斜率|\) 不升的下凸壳。
如图所示:当 \(p_1<p_2\) 时\(G_{i+1}\) 显然仍然满足凸壳性质。当 \(p_2<p_1\) 时 \(p_2\) 之前部分斜率绝对值变大,之后的部分斜率绝对值变小,同样满足凸壳性质。由于我们维护的是前缀 \(\min\),所以在后面有斜率为 \(0\),不会有后面翘起来的情况。
如何快速维护凸壳和最优 DP 决策点?考虑一种神仙做法:使用堆。堆中存储所有凸壳的拐点。关于刻画斜率:由于斜率绝对值单调不升,那么通过将同一个拐点多次插入堆模拟斜率。具体的,我们令堆中两个相邻位置构成的斜线 \([x_i,x_{i+1}]\) 的斜率为堆中 \(\ge x_{i+1}\) 的拐点个数。设插入 \(v\),我们考虑如何设计转移:
- \(v\ge q.top()\)
直接将 \(v\) \(push\) 进入 \(q\) 即可。答案不变。 - \(v< q.top()\)
答案增加 \(q.top()-v\)。同时凸壳中 \(\ge v\) 的部分斜率都 \(+1\),那么需要弹出最大的拐点(相当于给 \(\le q.top()\) 的部分斜率 \(+1\)),然后 \(push\) 两次 \(v\),相当于给凸壳中 \(\le v\) 的部分斜率 \(-2\)。
容易拓展到树上。
Day 9 8.25
T1
思维水平不高导致的。
满足 \(a_1\le a_2\le a_3\le \cdots \le a_n\),等价于判断是否任意两个相邻位置都满足条件.
考察一个相邻位置带来的约束:二进制中在从大到小第一个两者不同的位置上会给 \(x\) 一个约束:那个位置必须填 \(0/1\).
然后我们记一下 \(F_{i,0/1}\) 表示存在第 \(i\) 位必须填 \(0/1\) 这样要求的位置的数量.然后贪心计算答案即可.每次修改时,只会和影响前后两个相邻位置的判断.那么我们直接暴力修改即可.
总时间复杂度为 \(O((n+m)\log w)\).
T2
搜索剪枝.
T3
字符串.根号分治.
将 \(B\) 中串按串长和 \(K\) 比较分治.
串长 \(\ge K\) 的串 \(\le |B|/K\) 个.对于这部分,考虑使用 kmp 确定每个位置可以匹配的最长前缀和最长后缀.使用线段树区间转移.这一部分的复杂度为 \(O(n\log n|B|/K)\).
串长 \(<K\) 的串,建出 trie 后对于 \(n\) 中的每个位置暴力枚举 LCP 和 LCS 的长度.这一部分的时间复杂度为 \(O(nK)\).

浙公网安备 33010602011771号