2025.7 广大附中集训游记

经过一阶段校内集训,然后简单过完唯一一天暑假(也是我生日)之后,第二天(\(\text{Jul }20\text{th}\))也是被拉去广大附中继续集训坐牢两个星期了。

\(\text{Jul }20\text{th}\)

早上 \(8\) 点到达柳州站,第一件事先去打报销凭证……牛魔的这票的质量怎么这么差?早知道不打了!

车次是柳广原神 \(\text{D}3753\),车型是宁局原神 \(\text{CRH2A}\),本务 \(4114\)\(4034\) 重联,一进车厢就又能闻到那熟悉的镜酱的脚臭味了……在车上尝逝打音游但一会之后就放弃了。路途不远,\(3\) 个半小时就到广州了,下车之后就坐滴滴来了广大附中。

这学校嘛,布局看着挺不错的,也很生态,就是各种地方都在施工,总给人一种半成品的感觉?我去什么条件啊,宿舍楼装电梯?还有球场? 不愧是牢广,微距了。总之到了宿舍之后一整个下午都尝逝睡觉回蓝,但新环境拼尽全力无法睡着,就这样迷迷糊糊去上晚自习了。其实晚自习上还一直在打之前学的莫队www

\(\text{Jul }21\text{st}\)

\(0721!\)(喜)

牛魔的,昨天晚上一点没睡好,困死我了。第一天讲构造,一个早上也是拼尽全力无法不打瞌睡,只能说 \(12\) 道题大概也就听懂一半多点吧,不过也确实有一些能总结的地方。

\(1.\) 对于有 UDLR(上下左右)操作的题目,可以考虑将移动旋转 \(45°\) 后放大为 \(\sqrt 2\) 倍,这样就能转化成 \((1,1),(1,-1),(-1,-1),(-1,1)\) 这样两个方向上都有移动的操作,就能够独立两个维度来考虑了。对于一个维度,只有 \(-1\)\(+1\) 两种操作。然后我们考虑在坐标中加上时间戳(即每次移动都额外向正方向再移动一步),这样就变成了 \(0\)\(+2\) 两种操作。最后,再缩小成原来的 \(\dfrac{1}{2}\) 倍,就成功转化为了 \(0\)\(+1\) 两种操作。CF538G Berserk Robot

\(2.\) 对于给定图的构造,因为图的性质较少,而树的性质较多,可以考虑提出 \(\text{dfs}\) 树或者其他的什么树来分析。P7320 「PMOI-4」可怜的团主 用到了树的两个性质:

  • 覆盖一棵树需要 $\ge \left \lceil \dfrac{叶子数}{2} \right \rceil $ 条路径,当且仅当所有路径的端点为叶子时取等(当路径数为奇数时,至多一条路径可以只有一端为叶子)。
  • 一棵树的所有叶子可以构成一个独立集(互相没有边连接)。

\(3.\) 对于树的刻画,“无环”优于“连通”。CF611H New Year and Forgotten Tree

\(4.\) 给出 \(n\) 个点的完全图,可以考虑归纳第 \(i\)\(n\) 个点与前 \(i-1\) 个点的关系。ARC131E Christmas Wreath

\(5.\) 两个完美匹配取并,得到若干偶环。CF741C Arpa’s overnight party and Mehrdad’s silent entering

牢广这块物价真的是贵的离谱,广大附中食堂自选快餐 \(2.28\)\(/ 50\text{g}\),我已经收着力打了,却还是直接给我干到 \(27.45\) 元了,这都够我在树高吃两三顿了……最离谱的是我看这个学校里面的人打的饭菜也跟我差不多量,合着广爷搁学校里都顿顿 \(30\) 块,那出了学校不得贵死啊?

下午和晚上拼尽全力只能改出 \(3\) 题……

欸?意外收获?我的机位居然有一个野生的三月七 \(\text{badge}\),是 \(\text{QuAn}\) 酱画的那个头像。也不管是哪位学长的遗产,不过现在是我在这个机位,只希望她接下来两个星期能安慰一下我的心灵吧。

\(\text{Jul }22\text{nd}\)

这回倒是睡好了,但是今天讲数据结构,果不其然中途又打瞌睡了……总共 \(17\) 道题,\(4\) 题线段树 \(+\) \(3\) 题笛卡尔树 \(+\) \(2\) 题字典树 \(+\) \(8\) 道杂题(甚至其中 \(7\) 道都是黑!)笛卡尔树我之前是一点都没接触过的说……听懂一半算我输。还是先来个总结吧:

\(1.\) 序列 \([l,r]\) 排序后可以构成公差为 \(k\) 的等差数列,当且仅当:

  • 该序列的极差等于 \(k(r-l)\),这通过线段树维护区间最值实现查询。
  • 该序列的差分序列最大公因数为 \(k\),这通过线段树维护差分序列的区间最大公因数实现查询。
  • \(k\neq 0\) 时,该序列的数互不相同,这通过 unordered_map<int, set<int>> 维护每个数上一次出现的位置实现判断,其中 set 是某个值出现位置的集合。

P5278 算术天才⑨与等差数列

\(2.\) 一棵包含 \(k\) 个点的无标号有根树可以使用一个长度为 \(2(k - 1)\) 的括号序列表示。它们本质上等价于欧拉序(进入和退出节点时时间戳都增加)中通过一条边搜索以及回溯的过程,左括号对应深度 \(+1\),右括号对应深度 \(-1\)。根据括号序列计算深度前缀和序列 \(\{d_n\}\),那么 \(d_i\) 对应就是欧拉序中第 \((i+1)\) 个点的深度(第一个点是根),序列中区间 \([l,r]\) 的最小值就代表欧拉序中第 \((l+1)\) 和第 \((r+1)\) 个点的 \(\text{LCA}\) 深度。所以树上两点 \(u,v\) 的距离 \(\text{dis}(u,v) = \text{dep}_u + \text{dep}_v - 2\text{dep}_{\text{LCA}(u,v)}=d_{l-1}+d_{r-1}-2\min\limits_{i=l-1}^{r-1}d_i\),其中 \(l,r\) 分别为 \(u,v\) 的欧拉序。CF1149C Tree Generator?

\(3.\) 笛卡尔树:一种简化的 \(\text{Treap}\)。对于序列中的某个节点,其在这个序列的笛卡尔树上的键值(节点编号)等于下标,优先级等于它的数值。笛卡尔树分大根形态和小根形态,对于序列 \(\{a_n\}\) ,区间 \([l,r]\) 中的最小值等于小根笛卡尔树上 \(\text{LCA}(l,r)\) 的优先级(数值),最大值等于大根笛卡尔树上 \(\text{LCA}(l,r)\) 的优先级(数值)。笛卡尔树的建树是 \(O(n)\) 的。P5854 笛卡尔树的建树模板

笛卡尔树的应用场景:区间最值(一般用线段树,因为笛卡尔树并不平衡),序列中某个数左/右侧第一个大于/小于它的数等。

这回倒好,就只改出了第一题,交上去的 \(\text{AC}\) 代码还被 \(\text{Hack}\) 掉了……

晚上又讲了一些线段树理论,发现其实树高那会讲的其实还有些纰漏,于是就把树高那部分删去了,这里再重新总结一下。

\(1.\) 半群线段树(不需要懒标记)

可以维护一个序列 \(\{a_n\}\),其中 \(a_i \in D\),\((D,*)\) 是一个半群。它可以实现:

  • 单点修改:给定 \(i,x(1\le i \le n,x \in D)\)\(a_i \leftarrow x\)
  • 区间查询:给定 \(l,r(1\le l \le r \le n)\),求 \(*_{i=l}^r a_i\)

半群要求 \(*\) 运算满足结合律。

常见问题:带单点修改的区间求和/求积/求矩阵积/求最大子段和。

\(2.\) 环线段树(需要懒标记)

可以维护一个序列 \(\{a_n\}\),其中 \(a_i \in D\),\((D,*,\otimes)\) 是一个环。它可以实现:

  • 区间修改:给定 \(l,r,x(1\le l \le r\le n,x \in D)\),对于所有 \(i(l\le i \le r)\)\(a_i \leftarrow x * a_i\)
  • 区间查询:区间查询:给定 \(l,r(1\le l \le r \le n)\),求 $ \bigotimes\limits_{i=l}^r a_i$。

环要求 \(*\) 运算满足结合律,\(\otimes\) 运算满足结合律和交换律,且 \(*\) 运算对 \(\otimes\) 运算满足分配律。

相比于半群线段树,环线段树的一个节点 \(i\) 上,除了要维护 \(S_i \in D\) 外,还需要维护一个 \(T_i \in D\),即所谓的懒标记,其意味着节点 的子树仍然有一些修改尚未下传,这些修改等效于左乘 。通常而言,我们约定 \(S_i\) 表示的是不考虑 \(i\) 的祖先上的懒标记的修改时,
区间内的 \(a_i\) 之和 \((\otimes)\)。修改或查询时,当要递归到 \(i\) 的左右儿子时,更新左右儿子 \(S,T\) 的值,然后将 \(T_i\) 重置为幺元。

\(3.\) 标记永久化

在一些特殊的场景中(比如树套树),可能受时间或空间限制,也可能受某些规则的约束,我们无法下传标记,此时需要进行标记永久化,也就是标记不下传。为了做到这点,我们必须保证 \((D,*)\) 不仅仅是一个半群,而且是一个,即需要有乘法 \((*)\) 单位元和逆元。

假设我们需要给区间 \(i\) 整体左乘 \((*)\) 一个值 \(x\),它的祖先上有 \(T_a,T_b\) 两个懒标记,由于 \(S_i\) 表示的是不考虑 \(i\) 的祖先上的懒标记的修改时的区间和,其真实值 \(S'_i=T_a*T_b*S_i\),修改后就是 \(S''_i=x*T_a*T_b*S_i=T_a*T_b*(T^{-1}_a*T^{-1}_b*x*T_a*T_b)*S_i\),所以只需令 \(x'=T^{-1}_a*T^{-1}_b*x*T_a*T_b\),即可 \(S_i \leftarrow x'*S_i,T_i \leftarrow x'*T_i\)

看着好像是在《你搁这搁这呢?》实际上只是对于区间加来说,乘上逆元直接抵消掉了。但实际上,有些时候乘上逆元并不能直接抵消。

\(4.\) 值与标记的分离

其实很多时候,所谓“环线段树”并不能很好地描述我们要做的事情,而是得把维护的值 \(S\) 和懒标记 \(T\) 认为是不同的“东西”。也就是说,我们有值集合 \(V\) 和标记集合 \(A\),并且有运算:

  • \(V \leftarrow V * A\) ——给值“加上标记”的结果
  • \(A \leftarrow A * A\) ——标记的复合(满足结合律)
  • \(V \leftarrow V \otimes V\) ——值的“求和”

一个经典例子:\(V\)\(n\) 维向量集合,\(A\)\(n\times n\) 的矩阵集合。其实 \(n\) 维向量完全可以也看成 \(n\times n\) 的矩阵,从而在数学上将值与标记统一,分离仅仅是为了思维的方便。

如果问题的操作都是线性变换,那么可以直接维护 \(n\) 维向量,将 \(n\times n\) 的转置矩阵作为懒标记。矩阵与其乘法、加法是构成环的,所以可以用环线段树维护。

常见问题:

  • 区间加/区间乘/区间求和:维护二维向量 $ \begin{bmatrix} a_i \ 1 \end{bmatrix}$ 的和,每次区间 \(+x\) 相当于左乘上一个 \(2\times 2\) 的矩阵 \(\begin{bmatrix} 1 & x \\ 0 & 1 \end{bmatrix}\),区间 \(\times x\) 相当于左乘上 \(\begin{bmatrix} x & 0 \\ 0 & 1 \end{bmatrix}\)
  • 区间加/区间历史版本和:维护三维向量 $ \begin{bmatrix}a_i \ h_i \ 1 \end{bmatrix} $ 的和,区间 \(+x\) 相当于左乘上 $\begin{bmatrix}1 & 0 & x \ 0 & 1 & 0 \ 0 & 0 & 1 \end{bmatrix} $,累加一次版本 \((h_i \leftarrow h_i + a_i)\) 相当于左乘上 \(\begin{bmatrix} 1 & 0 & 0 \\ 1 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix}\)

但是,这些经典问题大多用不上线性代数的做法。因为可以观察到,我们所维护的向量,以及转置矩阵含有大量的 \(0\)\(1\),这得以让我们把它们优化成单变量懒标记。同时,矩阵变换的时间常数极大,效率会略逊于单变量懒标记。但线性代数的做法可能更好想(只要能看出所有操作都能通过线性变换实现),正确性和时间复杂度是得以保证的,对于非经典问题,尤其是在考场上,如果想到了线代做法,就应该立刻打上。尽管最后可能因为极大的常数而丢掉一两个点,但这总比干瞪眼瞪不出单变量懒标记的优化而爆零好太多了。

\(\text{Jul }23\text{rd}\)

今天上午是 \(\text{NOIP}\) 模拟赛。

\(\text{T}1\) 本来只打了个 \(40\) 分的暴力,后面发现是很简单的 \(\text{DP}\),期望得分 \(100\text{pts}\),实际得分:\(20\text{pts}\) ——啊??怎么爆了??还不如打暴力呢 死因:做法假了,不是 \(\text{DP}\),实际上还真只需要暴力就可以了,我之前的暴力不对www。

修改后得分:\(100\text{pts}\)

\(\text{T}2\) 不会写,输出个无解看看能得多少,期望得分 \(1\text{pts}\),实际得分:\(0\text{pts}\) ——沟槽的捆绑测试

修改后得分:\(100\text{pts}\)

\(\text{T}3\) 暴搜乱搞,期望得分 \(20\text{pts}\),实际得分:\(20\text{pts}\)

\(\text{T}4\) 暴力乱搞,期望得分 \(15\text{pts}\),实际得分:\(15\text{pts}\)

期望得分(总):\(136\text{pts}\),实际得分:\(55\text{pts}\)\(\text{Rank}:69/72\)

修改后得分:\(235\text{pts}\)

只能说考的一坨,\(\text{Rank}\) 垫底,\(\text{T}1\)\(\text{T2}\) 本该拿到更高的分数,甚至应该全切,但没有做到。不过 \(\text{T3}\)\(\text{T4}\) 倒是预期完成了,骗到 \(35\) 分已经可以算是收获达标了,在考场上就应该这样。如果 \(\text{T}1\)\(\text{T2}\) 稳定发挥,那么这个分也算是能完成省一的夙愿了。

\(\text{Jul }24\text{th}\)

今天讲 \(\text{DP}\)全都听不懂! 直接开摆,写基础的橙黄 \(\text{DP}\) 题去了。数据结构讲蓝紫黑题就算了,给我讲 \(\text{DP}\) 蓝紫黑题我是真的会死的。

下午勉强把 AGC061C First Come First Serve 大概弄懂了,但仅此而已。

有点新东西:

\(1.\) 二项式反演:

\[f(n)=\sum_{i=0}^n \binom n i g(i) \Rightarrow g(n)=\sum_{i=0}^n(-1)^{n-i} \binom n i f(i) \]

囿于篇幅不作证明。

常见于 \(f(n)\) 表示“至多 \(k\) 个元素满足条件”的计数,\(g(n)\) 表示“恰好 \(k\) 个元素满足条件”的计数。大多数情况下,\(f(n)\) 是较为好求的,而想直接计数求解 \(g(n)\) 比较困难,此时可以考虑用反演来求得。

经典问题:P1595 错位全排列,即求 \(n\) 个元素全不放在原位 \((\forall i \in [1,n], a_i \neq i)\) 的排列数。

\(g(n)\) 表示恰好有 \(n\) 个错位的排列数,\(f(n)\) 表示至多有 \(n\) 个错位的排列数,也就是不带任何限制的排列数,所以 \(f(n)=n!\)。根据二项式反演,有

\[g(n)=\sum_{i=0}^n(-1)^{n-i} \dbinom n i i!=\sum_{i=0}^n \frac{(-1)^i n!}{i!} \]

这便是经典的错位全排列公式。通过预处理出 \(1\)\(n\) 的阶乘,这个式子的结果就可以 \(O(n)\) 算出,这对于链接里 \(1 \le n \le 20\) 的数据范围完全是大炮轰蚊子。如果作为二项式反演的板子,应该带上大数取模和线性递推乘法逆元开到 \(1\le n \le 10^8\) 量级。

二项式反演还有另一种形式:

\[f(k)=\sum_{i=k}^n \binom i k g(i) \Rightarrow g(k)=\sum_{i=k}^n(-1)^{i-k} \binom i k f(i) \]

常见于 \(f(k)\) 表示“至少 \(k\) 个元素满足条件”的计数,\(g(k)\) 表示“恰好 \(k\) 个元素满足条件”的计数。

P4859 已经没有什么好害怕的了\(\text{DP}\) 出“至少”的情况,然后用反演求出“恰好”的情况。

\(2.\) \(K\)-\(\text{D}\) 树:是一棵可以存储 \(K\) 维空间中的点信息,支持插入、删除和高维空间中对数级时间复杂度搜索的平衡二叉树,树上每个节点对应 \(K\) 维空间中的一个点。当 \(K=2\) 时,能够实现将二维平面转换成二叉树,快速查询平面最近点对 (P1429)。建 \(K\)-\(\text{D}\) 树通常用维度轮换法或最大方差法,囿于篇幅不作展开。

今天真的是倒霉完了,其实一开始说好每天 \(2\) 小时放风玩玻璃砖的,\(\text{Jul 20th}\) 晚上又说只有周末才能玩,给我闷了三天。好不容易今天晚上 \(20\) 分钟放风,本想着把 \(\text{Codeforces}\) 账号弄好,结果网站刚好被 \(\text{Bad Gateway}\) 在外面进不去,真的是麻了。

\(\text{Jul 25th}\)

宿舍楼一大早上起来就放大石碎胸口,鉴证英雄这一块。

今天讲 \(\text{DP}\) 优化,继续开摆,写黄绿 \(\text{DP}\) 题。

下午有点受不了了,奖励了自己两三道线段树问题的板子。

什么?体锻时间比 \(400 \text{m}\)?第一名送机械键盘?\(\text{byd}\) 那么热的天谁爱跑谁跑去吧,反正我直接一个弃权。什么?推迟啦?好似喵~

晚上 \(\text{Codeforces}\) 能进了,还好之前放风的时候登上了 \(\text{QQ}\) 这下就有邮箱了,但现在虽然注册好了,洛谷这边反倒还绑不上(绷)。

\(\text{Jul 26th}\)

宿舍楼一大早上起来又放了一遍大石碎胸口,真的是吉列的豆蒸。

今天上午是第二场模拟赛。

\(\text{T1}\):暴力乱搞,期望得分:\(10\) ~ \(35 \text{pts}\),实际得分:\(\text{0pts}\)怎么一分没骗到?? 死因:题意根本没懂。实际上,这就是个黄题

修改后得分:\(\text{100pts}\)

\(\text{T2}\):暴力 \(\text{DP}\),期望得分:\(\text{30pts}\),实际得分:\(\text{30pts}\)

\(\text{T3}\):暴力乱搞,期望得分:\(\text{25pts}\),实际得分:\(\text{25pts}\)

\(\text{T4}\):摆了。期望得分:\(\text{0pts}\),实际得分:\(\text{0pts}\)

期望得分:\(\text{}\) \(\text{65}\) ~ \(\text{90pts}\),实际得分:\(\text{55pts}\)\(\text{Rank}:36/42\)

修改后得分:\(\text{155pts}\)

考的和上一场一样差(甚至分数都一模一样),除了第一题外其他题目一共骗到了 \(\text{55pts}\),和上一场问题一样,有难度的题目能骗到分,但简单题切不掉,考虑日后方向转向黄绿基础题,并且多打一点基础赛。

要是再这么下去我真的想 \(\text{AFO}\) 了,闹麻了都,花这么多精力有个几把用啊?还不如好好搞 \(\text{whk}\)……

下午讲博弈论。暑假真的是各种不方便,下午因为停电还得换机房……两个机房的人凑在一个机房里面,空调还不给力,热死了。这下子变成群魔乱舞了,一排过去同时坐了:图寻大蛇(平均 \(5\) 秒一道题,正确率高达 \(90\%\)),魔方大蛇(粗略估计有 \(\text{sub15}\) 的水平),文言文大蛇(拿文言文资料出来看结果被教练抓包),洛谷领域大蛇(主页及其花里胡哨),异史氏曰:梅逸阁诗人。

博弈论这块没怎么听,讲题人默认我们熟练了一系列博弈论的基础模型,但实际上我之前博弈论的几个简单模型是自学的,而且学的不深,再加上没再去看,基本上忘得差不多了……可笑的是,我唯一印象深刻的就是讲题人在课件开头整的活:

\(何为博弈论?\)

形式保守 形式中立 形式自由
内容保守 \(\text{Nim}\) 公平组合游戏 递推 \(\text{SG}\) 函数 \(\text{Surreal Number}\)
内容中立 围棋 狼人杀 贪心也是一种博弈
内容自由 躲猫猫 \(\text{QQ}\) 群里对线也是一种博弈 《请输入文本》

\(\Huge广州蚊子真的好多啊!\)

我怀疑我的花露水被掉包成了蚊子信息素一类的东西。

考虑到今天模拟赛的总结,晚上 \(8\) 点打一场 \(\text{ABC416}\),看看自己什么货色。

评价为:小学生都不如。\(\text{ABC}\) 级别的比赛,场上只写出来 \(\text{A}\)\(\text{C}\)\(\text{Rank}\ge7000\),太丢人了,可以期待今年炸成什么样了。

赛后切掉了 \(\text{B,D,E}\)

\(\text{Jul 27th}\)

宿舍楼一大早上起来又放了一遍大石碎胸口,三回啊三回。

今天是休息日,改一下昨天 \(\text{ABC416}\) 的题目。以后的话就是考虑以打基础赛为主了,剩下三个月,尽量加练一点 \(\text{ABC}\)\(\text{CF Div3}\)。前两场模拟赛,\(\text{T1}\) 之后的题目都骗到了 \(55\text{pts}\),如果 \(\text{T1}\) 能切,那么在 \(\text{GX}\) 就是一个很可观的分数,现在我是基础这块太薄弱了。

不过既然是休息日,还是能出去玩就出去玩一下。今天去了天河、正佳、地王三个二次元资源点,其中天河甚至做到一个 \(\text{IP}\) 开一家店,而浓度最高的是地王,特别的全面,甚至连 \(\text{Gal}\) 的浓度都很高,这些都是柳州不可能有的事情,我整个跟农村的进城里一样(当然事实上也的确算是了)。最后是买了两个 \(\text{Badge}\),一个 \(\text{Ako}\) 的,一个京吹的(可惜的是最后京吹抽到的是高黄,我只能说鬼图打码)。

午饭这块不得不赞扬一下,当时我们在正佳里面,我们想着商场里面的各种餐厅,一来麻烦,二来贵得要死,想着同样都是贵的,还不如吃麦门。而倒霉的是当时商场里面一共两家麦门全都爆满了。天无绝人之路,我们在导航上发现:有一家萨莉亚离我们距离只有 \(700\text{m}\),那一刻我们仿佛看到了光。去了之后终于明白为什么广西那么多人求萨莉亚开一家在广西了:跟学校食堂一个价,味道不比学校食堂差,饮料还可以无限续杯,这谁看了不迷糊,都这样了还要什么自行车?

晚上的话就是把之前的一些题目补上,同时因为明天讲线代,再去把 \(\text{3b1b}\) 的视频补一下。

\(\text{Jul 28th}\)

宿舍楼一大早上起来又放了一遍大石碎胸口,大四喜这一块。

今天上午讲线性代数,虽然这个东西之前都或多或少有接触,但新东西也有点多,着重于会在 \(\text{OI}\) 中应用的知识点,而非大学数学学科中严谨而全面的线代内容。

\(1.\) 行列式

“行列式”仅在方阵中有定义,三行四列的行列式这个东西是不存在的。\(n\) 阶方阵 \(A\) 的元素构成的行列式,称为 \(A\) 的行列式,记作 \(\det A\) 或者 \(|A|\)。设 \(p\) 为长度为 \(n\) 的一个排列,其奇偶性(排列中逆序对个数的奇偶性)为 \(\tau(p)\)\(S\) 表示长度为 \(n\) 的全排列,则

\[\det A=\sum\limits_{p\in S} (-1)^{\tau(p)} \prod\limits_{i=1}^n A_{i,p_i} \]

几何意义为方阵 \(A\) 中列向量所张成的 \(n\) 维平行多面体的有向超体积(行列式有正负)。当 \(n=2\) 时,其绝对值表示列向量所张成的平行四边形的面积;当 \(n=3\) 时,其绝对值表示列向量所张成的平行六面体的体积。

但是,上面那个计算行列式的式子一眼看过去就觉得复杂度很恐怖:\(O(nn!)\)。事实上,在 \(\text{OI}\) 中我们并不会这样计算行列式。但通过这个式子,我们可以发现:上/下三角矩阵的行列式 \(\det A =\prod\limits_{i=1}^n A_{i,i}\),即对角线上元素的乘积。而 \(\text{OI}\) 中计算行列式的方法,就是先用高斯消元将普通的方阵变成上三角矩阵,然后再把对角线上的元素乘起来,复杂度为 \(O(n^3)\)。这样做的正确性基于行列式的性质:

  • 交换方阵中的两行/列,行列式变号。
  • 将方阵中某一行/列的元素全都数乘上 \(k\),最后行列式的值也扩大为原来的 \(k\) 倍。
  • 把行列式的某一行/列的各元素乘同一数然后加到另一行/列对应元素上去,行列式不变。

P7112【模板】行列式求值

\(2.\) 矩阵的逆

对于方阵 \(A\)(没错还是对于方阵),如果有一个同阶的方阵 \(B\) 满足:\(AB=BA=E\),其中 \(E\) 为单位矩阵,则说明方阵 \(A\) 可逆的,并把方阵 \(B\) 称为方阵 \(A\) 的逆矩阵,记为 \(A^{-1}\)。方阵 \(A\) 可逆当且仅当 \(\det A\neq 0\)

求逆的时候,维护两个初始矩阵 \(A\)\(E\)。因为 \(A^{-1}A=E,A^{-1}E=A^{-1}\),所以用高斯-约当消元将 \(A\) 化为单位矩阵 \(E\),同时\(E\) 也进行同样的操作,这样当 \(A\) 化为 \(E\) 时,\(E\) 也被化为了 \(A^{-1}\),也就是我们所求的逆。

P4783 【模板】矩阵求逆

\(3.\) 线性基

称一个 \(n\) 维向量组 \(\{\bm{v}_i\}\)线性无关的,当且仅当不存在不全为零的一组数 \(\{c_i\}\),使得 \(\sum c_i \bm{v}_i = \bm{0}\)。而线性基认为是一个 \(n\) 维向量集合中极大的线性无关向量子集。可以证明任何向量集合存在线性基,且一个向量集合的任意线性基大小相同。其实线性基可以理解为高中数学中所说的“基底”。

那么,如何构造一组线性基呢?显然,线性基最多只会有 \(n\) 个元素。维护一个向量组 \(\{\bm{a}_i\}\) 来表示最终得到的线性基。遍历组中每一个向量 \(\bm{v}\),从高到低扫描每一维。如果 \(\bm{v}\) 的第 \(i\) 位非零,那么就检查 \(\bm{a}_i\)

  • 如果 \(\bm{a}_i\) 不存在,那么令 \(\bm{a}_i \leftarrow \bm{v}\),退出循环。
  • 如果 \(\bm{a}_i\) 存在,那么令 \(\bm{v} \leftarrow \bm{v}-\dfrac{\bm{v}_x}{\bm{a}_{i,x}}\bm{a}_i\),继续检查下一位。

上述算法的时间复杂度为 \(O(n^2m)\),其中 \(m\) 为遍历的向量数量。同时,如果一个向量可以通过上面的过程循环到最后一位,最终变为的零向量 \(\bm{0}\),说明该向量可以被这一组线性基表示。

P3265【模板】实数线性基

一般情况下,相较于 \(n\) 维实线性空间 \(\mathbb{R}^n\) 下的实数线性基,\(\text{OI}\) 中研究的更多的是 \(n\) 维布尔域线性空间 \(\mathbb{Z}_2^n\) 下的异或线性基。在布尔域线性空间中,一个向量等价于一个 \([0,2^n)\) 内的整数,加法等价于按位异或,数乘等价于“且”。在 \(n\) 维布尔域线性空间下,一个数能够被线性基表示,等价于可以被表示成数集中若干个数的异或和,也就是数集的子集异或和

利用异或线性基,能够解决如下的问题:

  • 检验一个数是否能表示成某个数集的子集异或和,以及方案数。
  • 一个数集能够表示的子集异或和的数量/第 \(k\) 大值。

异或线性基的构造与实数线性基的构造相似。
遍历组中每一个向量 \(\bm{v}\),从高到低扫描每一维。如果 \(\bm{v}\) 的第 \(i\) 位非零,那么就检查 \(\bm{a}_i\)

  • 如果 \(\bm{a}_i\) 不存在,那么令 \(\bm{a}_i \leftarrow \bm{v}\),退出循环。
  • 如果 \(\bm{a}_i\) 存在,那么令 \(\bm{v} \leftarrow \bm{v} \oplus \bm{a}_i\),继续检查下一位。

由于异或可以通过位运算优化,上述算法的时间复杂度为 \(O\bigg(\dfrac{n^2m}{w}\bigg)\),在 \(n \le 64\) 的时候一般认为时间复杂度为 \(O(nm)\)

P3812【模板】异或线性基

\(4.\) \(\text{LGV}\) 引理

这玩意居然在b站搜不到相关视频!就连洛谷上也只有4道题打了这个标签,听说最早出现于NOI2021,瞬间就不想学了……打个模板然后跑路算了。

对于有向无环图 \(G\),定义一条路径 \(P\) 的权值 \(w(P)\) 为路径上所有边的边权积,对于图中两点 \(u,v\),定义 \(f(u,v)=\sum\limits_{P:u \rightarrow v} w(P)\) 表示 \(u,v\) 间所有路径的权值和。对于两个大小为 \(n\) 的点集 \(S,T\),一组从 \(S\)\(T\) 的不交路径 \(P\) 被定义为:\(P_i\) 是一条从 \(S_i\)\(T_{\sigma(P)_i}\) 的路径,其中 \(\sigma(P)\) 为一个与路径组 \(P\) 对应且长度为 \(n\) 排列,且 \(\forall i\neq j,P_i \cap P_j = \emptyset\),即路径无交点。

\(\text{LGV}\) 引理指出,对于矩阵 \(M = \begin{bmatrix} f(S_1,T_1) & f(S_1,T_2) & \cdots & f(S_1,T_n) \\ f(S_2,T_1) & f(S_2,T_2) & \cdots & f(S_2,T_n)\\ \vdots & \vdots & \ddots & \vdots \\ f(S_n,T_1) & f(S_n,T_2) & \cdots & f(S_n,T_n) \end{bmatrix}\),有

\[\det M = \sum_{P:S\rightarrow T} (-1)^{\tau(\sigma(P))} \prod_{i=1}^n w(P_i) \]

囿于篇幅不做证明。简单来说,\(\det M\) 本是对从 \(S\)\(T\) 的任意路径组(没有不交限制)的带符号求和,但对于每一个有交的路径,其对于 \(\det M\) 的贡献都会被抵消掉,因此剩余的就是不交路径组。

由于是对于不交路径组的带符号求和,所以 \(\text{LGV}\) 引理难以直接统计所有不交路径组的权值和 \(\bigg(\sum\limits_{P:S\rightarrow T} \prod\limits_{i=1}^n w(P_i)\bigg)\) 。因此,在实际问题中,有如下几种解决方案:

  • 题意要求统计的就是带符号和。
  • 题目中的图为特殊图,使得不交路径的起点和终点对应是固定的,只存在一种或奇偶性相同的几种 \(\sigma(P)\)
  • 只需要检验不交路径组的存在性,给边随机赋权,查询 \(\det M\) 是否非零。

P6657 【模板】LGV 引理

CF348D Turtles

今天内容相对来之前来说比较简单,能写到的题数也比之前多了一点。晚上整了一个吃西瓜的活动,瓜其实不错,够水够甜籽不多,就是没早点说,不然我晚饭可以不用吃那么饱。吃了两片就跑路了。

\(\text{Jul 29th}\)

宿舍楼一大早上起来又放了一遍大石碎胸口,五福临门这一块。

今天上午是杂题选讲,直接开摆!

下午楼上开始灌水泥了,那叫一个震感强烈啊,还好我把降噪耳机带来了,效果显著,没枉费我两三百块钱。

\(\text{Jul 30th}\)

宿舍楼一大早上起来又放了一遍大石碎胸口,六六大顺这一块。

杂题选讲,继续开摆。

下午是本来是个人 \(\text{ACM}\) 赛制比赛,三个半小时七道题。但是《应隔壁机房的强烈要求》,在开赛前两分钟临时改成了 \(\text{IOI}\) 赛制。这一场是连乱搞都搞不明白了,已经没有做报告的必要了。拿到了 \(\text{8pts}\) 的郝成绩,\(\text{Rank 79/84}\)。赛后改了 \(\text{T2}\)\(\text{T3}\)

其实 \(\text{T3}\) 也不能算全改完了吧,就是不知道为什么多数组线段树的 \(\text{AC}\) 代码手动封装成结构体之后就 \(\text{RE}\) 了……

\(\text{Jul 31st}\)

宿舍楼一大早上起来又放了一遍大石碎胸口,七圣召唤这一块。

今天讲字符串。

\(1.\) \(\text{Trie}\)/字典树

\(\text{Trie}\) 是一棵有向树,每个节点对应一个字符串的前缀(叶子表示一个完整字符串),根表示空串。每条有向边 \(u \rightarrow v\) 标记一个字符 \(c\),表示 \(v\) 所代表的前缀是在 \(u\) 所代表的前缀后面加上字符 \(c\) 得到。因此 \(\text{Trie}\) 有性质:两个串的最长公共前缀 \(\text{(LCP)}\) 对应节点为 \(\text{Trie}\) 上的 \(\text{LCA}\)

\(2.\) 自动机

自动机是什么?想象我们需要判断一个串是否满足某些性质(例如,包含 \(S\)/是 \(S\) 的子串/……)。一位一位地把这个串读进来。在这个过程中,我们维护一个状态,它记录了读进来的前缀与性质之间的信息。每次读入下一个字符后,我们根据当前状态和这个字符转移到下一个状态。这样的状态转移就被称为自动机。

例如我们需要判断一个 \(01\) 串中 \(1\) 的个数的奇偶性(以下作为 \(01\) 串的奇偶性),我们可以这样构建自动机:建两个节点 \(0\)\(1\),分别表示奇和偶两个状态。当我们一位一位地读进 \(01\) 串时,如果读到 \(0\),不会改变当前读入前缀的奇偶性,所以我们在这两个节点分别建一个自环,表示读入 \(0\) 状态不转移(转移到自身节点)。每读到一个 \(1\),前缀的奇偶性都会改变一次,所以我们在这两个节点间互相建一条有向边,表示读入 \(1\)转移到相邻状态。这样便建好了一个简单的自动机。按位读入 \(01\) 串时,根据读入的字符决定状态是通过自环还是有向边来转移,最终达到我们的目标。可以发现,这个自动机的运作流程跟异或运算是相同的:异或 \(0\) 不改变结果,异或 \(1\) 变为相反结果,可以被称为“异或自动机”,\(01\)\(S\) 的奇偶性为 \(\bigoplus\limits_{i=1}^{|S|} S_i\)

\(3.\) \(\text{KMP}\) 算法与 \(\text{AC}\) 自动机

实际上,这两个东西是有联系的,\(\text{KMP}\) 算法本身就是一个能统计【一个串 \(S\) 在另一个串 \(T\) 中的出现次数】的自动机,只不过 \(\text{AC}\) 自动机能够同时统计很多个 \(S\) 串(在它们的 \(\text{Trie}\) 上),可以认为 \(\text{AC}\) 自动机就是 \(\text{Trie}\) 上的 \(\text{KMP}\)

考虑如何从 \(\text{KMP}\) 扩展到 \(\text{AC}\) 自动机。在 \(\text{KMP}\) 中,我们从小到大考虑每个前缀。对于一个前缀 \(S[1 : i]\),从上一个前缀 \(S[1 : i − 1]\) 开始向前跳 \(\text{fail}\),直到某个前缀能够扩展出 \(S_i\)。在 \(\text{AC}\) 自动机中,我们按长度从小到大考虑每个前缀(\(\text{Trie}\) 上的广搜)。对于一个节点(它表示一个前缀),从父亲节点开始向上跳 \(\text{fail}\),直到某个前缀(可以不属于现在这个串,即在 \(\text{Trie}\) 上的另一个子树中)能够扩展出 \(S_i\)(这个节点在 \(\text{Trie}\) 树上有对应的儿子)。

但是仅有 \(\text{fail}\),复杂度容易炸。因为向上跳 \(\text{fail}\) 找一个字符的扩展时,可能需要在 \(\text{fail}\) 树上一个地方重复跳多次。为避免重复,我们考虑把这个扩展的结果存下来,这就是 \(\text{AC}\) 自动机相较于 \(\text{KMP}\) 的一个新东西:转移边——对于每个前缀,\(\text{next}_c\) 表示向这个前缀加入字符 \(c\) 后,其最长的匹配某个前缀的后缀。如果 \(\text{Trie}\) 上它就有这个儿子,那么 \(\text{next}_c\) 指向这个儿子就好了。否则,按照之前的做法,我们沿着 \(\text{fail}\) 向上跳,直到有一个节点有这个儿子,\(\text{next}_c\) 就指向它。同样,在 \(\text{Trie}\) 上做 \(\text{bfs}\) 即可,复杂度 \(O(n|\Sigma|)\),因此字符集过大时不建议使用。

\(\text{AC}\) 自动机在统计子串出现次数时,还有一点与 \(\text{KMP}\) 有差异。只有一个串的时候,匹配成功当且仅当当前最长匹配到整串,这与 \(\text{KMP}\) 一致。但多个串的时候,有可能现在匹配了这个串,但同时匹配了别的串一个更长的前缀,这也是需要记录的。因此,每匹配一位,我们需要在 \(\text{fail}\) 树上,把当前点到根上的匹配次数都 \(+1\)。这样才能正确算出每个给定串的出现次数。这也相当于每个串的匹配次数是有多少次匹配停在它 \(\text{fail}\) 树子树内。那么在线询问可以 \(\text{dfs}\) 序+区间求和,离线可以最后一次树上子树和。

\(4.\) 子序列自动机

用于判断一个序列 \(T\) 是不是另一个序列 \(S\) 的子序列。

实际上相当于我们需要在 \(S\) 中选出一些递增的位置,使得它们拼起来等于 \(T\)。那贪心地想,我们应该让所选的位置都尽量靠前,如果能匹配,那这样一定能匹配到。因此判定方法是,记录匹配 \(T\) 的前缀需要选到的位置 \(\text{now}\),加入下一个字符后,选择 \(\text{now}\) 之后这个字符下次在 \(S\) 出现的位置。

根据上面所说的,我们可以构建出自动机:以 \(S\) 的下标为状态,以 \(\text{next}_{i,c}\) 表示位置 \(i\) 之后字符 \(c\) 第一次出现在 \(S\) 中的位置为转移。这样,沿着 \(\text{next}\) 能走出的所有序列正是 \(S\) 的所有子序列。

\(5.\) \(\text{Manacher}\) 算法与回文树、回文自动机

\(\text{Manacher}\) 算法,俗称马拉车,它能在线性时间复杂度内求出字符串中以任意位置为中心所能拓展出的最长回文子串的长度。(长难句这一块)

众所周知,回文串有奇回文(奇数长度)和偶回文(偶数长度)之分。奇回文串的中心是一个字符,但偶回文串的中心是\(\color{white}棍母\),这给我们解决问题带来不便。而马拉车做的第一件事情便是改造字符串:在字符串的首尾以及各个字符中间都添加上一个字符集之外的相同特殊字符,这样就把两种回文类型统一为了奇回文——奇回文串改造后的中心仍然是原字符,偶回文串改造后的中心为特殊字符,而不再是\(\color{white}棍母\)

定义 \(d_i\) 为以下标为 \(i\) 的字符为中心的最长回文子串的半径(例如 \(S=\)aabacaba中以 \(S_5=\)c为中心的最长回文子串abacaba半径为 \(4\),所以 \(d_5=4\)),令 \(l,r\) 表示当前右端点最靠右的回文子串的左右端点,以下称区间 \([l,r]\) 为“盒子”。

当拓展到 \(S_i\) 时,进行如下操作:

\((1)\)\(i\) 在盒子内(\(i\le r\)),找到 \(i\) 关于盒子中心的对称位置 \(j=r-i+l\),如果以 \(j\) 为中心的最长回文子串被包含在盒子内,则直接令 \(d_i \leftarrow d_j\),这利用了“回文的镜像仍然是回文”这一性质。否则,先令 \(d_i \leftarrow r-i+1\)(到盒子右端点的距离),然后再用中心拓展法暴力比对盒子外的部分,决定是否能继续更新 \(d_i\)

\((2)\)\(i\) 不在盒子内,直接用中心拓展法暴力比对。

\((3)\)\(i+d_i-1>r\),更新盒子左右端点。

这个过程中,产生复杂度的部分仅有对于盒外部分的暴力拓展,这使得盒子右端点 \(r\) 不断递增,最终到达字符串的最右端。因此,这个算法的时间复杂度是线性的。

最后,字符串 \(S\)(改造后的字符串为 \(S'\))中最长回文子串的长度即为 \(\max\limits_{i=1}^{|S'|} d_i-1\)

\(\text{Aug 1st}\)

漫无止境的八月到来了,吉列的豆蒸结束了。

\[八月对我好一点。 \]

——长门有希

今天接着讲字符串——后缀树、后缀数组和后缀自动机。由于这块讲后缀树和后缀自动机的部分有点不太能接受,暂时总结不出来……

后缀数组(\(\text{SA}\)

约定:后缀的编号为该后缀在原串中的起始位置,后缀的排名为所有后缀按字典序升序排序后的排名。

\(\text{SA}\) 包括三个数组sa[]rk[]height[],其中sa[i]表示排序后第 \(i\) 名后缀的编号,rk[i]表示编号为 \(i\) 的后缀的排名,前两者互为反函数,即rk[sa[i]]=sa[rk[i]]=iheight[i]表示第 \(i\) 名后缀与第 \(i-1\) 名后缀的 \(\text{LCP}\) 长度。如果我们想求任意两个后缀的 \(\text{LCP}\) 长度,取height[]的区间最小值即可。

\(\text{PS}\):实际上 \(\text{LCP}\) 的传递是不等关系 \(|\text{LCP}(S_i,S_j)| \ge \min\{|\text{LCP}(S_i,S_k)|,|\text{LCP}(S_k,S_j)|\}\),但这里已经按字典序排序了,所以如果相邻两串某个字符不同,那么跨越这里的两个串要么之前就有不同的,要么之前相同,从这个字符开始不同。所以对于 \(i<k<j\),上式必然取到等号。

一般情况下使用倍增法+桶排序求出sa[]rk[],复杂度为 \(O(n \log n)\),如果直接用sort()的话会多一个 \(\log\)。求出sa[]rk[]后,就可以 \(O(n)\) 求出 height[]

\(\text{SA}\) 的经典应用:

  • 在字符串 \(S\) 中查找最长重复子串,答案即为 \(\max\limits_{i=2}^{|S|}\)height[i]
  • 找字符串 \(S_1,S_2\) 的最长公共子串:只需将两个字符串拼起来,两个串中间用不属于字符集的特殊字符来连接,然后求最长重复字串就可以了。

此外,\(\text{SA}\) 还可以做字符串匹配或者找最长回文子串,不过前者一般用 \(\text{KMP}\),后者一般用马拉车。

P3809【模板】后缀排序

\(\text{Aug 2nd}\)

学生返校后的宿舍起床铃震耳欲聋,但第一首 \(\text{Artcore}\) 和第二首 \(\text{PIKASONIC}\) 的曲子给我溜爽了。

Artcore 天下第一!!!!!

今天讲非常规 \(\text{DP}\) 优化,开摆。

至此,两周的牢狱集训生活结束了。

posted @ 2025-07-27 07:45  4BboIkm7h  阅读(41)  评论(1)    收藏  举报