「2022/07」学习记录

2022/8/1 6:22:46

本文主要是介绍「2022/07」学习记录,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

本地备份。学了 \(2\) 个月文化课,7.20 才差不多回来。

重新学了一遍字符串,感觉还可以。

痛みと痛み取り替えよう,糧にするんだ 落花の欠片。

放弃的话就到此为止了,但是你可以改变命运,无法回避的毁灭与叹息,一切都有你来颠覆即可,你具有正式为此而生的力量。


「BJOI2020」封印

给出只包含小写字母 \(a,b\) 的两个字符串 \(s, t\),\(q\) 次询问,每次询问 \(s[l \dots r]\) 和 \(t\) 的最长公共子串长度。

其中 \(|s|,|t| \le 2 \times 10^3,q \le 2 \times 10^5\)。

先对 \(t\) 建出一个 SAM,不妨设 \(len_i\) 表示前缀 \(i\) 和 \(s\) 匹配的最大后缀。这个可以预处理出。于是答案为

\[\max\limits_{i=l}^r \{ \min(len_i,i-l+1) \} \]

拆开 \(\min\),当 \(len_i \le i-l+1\) 时,转化一下得 \(i - len_i +1 \ge l\)。当到 \(i+1\) 时,\(len_i\) 可能加一可能清零,这意味着这个东西是单调不降的。

这意味着 \(i-len_i+1\) 和 \(l\) 一定有一个交点 \(p\),满足 \(i \ge p\) 时,有 \(i-len_i+1 \ge l\)。

于是考虑二分出这个 \(p\),前半段答案就是 \(i-l+1\),后半段的答案就是最大 \(len_i\),取最大即可。

「CF87E」Mogohu-Rea Idol

按逆时针顺序给出三个凸包点集 \(A,B,C\),每次查询给出点 \(q\),问是否存在点 \(a \in A,b\in B,c \in C\) 满足 \(q\) 为 \(\vartriangle abc\) 的重心。

本题关键,别看错题。(?)指第一次没看到凸包第二次以为只有端点(

其次重心有 \(\overrightarrow{OP}=\frac{1}{3}(\overrightarrow{OA}+\overrightarrow{OB}+\overrightarrow{OC})\)。

到这步其实就可以一眼秒了。

这就是闵可夫斯基和的形式,所以对 \(3\) 个凸包做一个闵可夫斯基和然后判断一下询问是否在凸包内即可。

「COCI2015」Divljak

Alice 有 \(n\) 个字符串 \(\mathrm{S}_1, \mathrm{S}_2, \ldots, \mathrm{S}_n\),Bob 有一个字符串集合 \(\mathrm{T}\),一开始集合是空的。

接下来会发生 \(q\) 个操作,操作有两种形式:

  1. 1 P:Bob 往自己的集合里添加了一个字符串 \(\mathrm{P}\)。
  2. 2 x:Alice 询问 Bob,集合 \(\mathrm{T}\) 中有多少个字符串包含串 \(\mathrm{S}_x\)(我们称串 \(\mathrm{A}\) 包含串 \(\mathrm{B}\),当且仅当 \(\mathrm{B}\) 是 \(\mathrm{A}\) 的子串)。

对于所有数据,\(1 \le n,q \le 10^5, \sum |S_i| \le 2 \times 10^6\)。

注意到操作 \(2\) 是多串匹配,于是先对 \(S_x\) 建一个 AC 自动机。每次考虑对于一个新加的串 \(P\),考虑他对所有 \(S\) 的贡献。

然后注意到每次匹配时,到失配点时就是该串对这个匹配点的串的最大前缀。然后跳 \(fail_u\),再匹配下一个点。于是考虑把 \(fail\) 树建出来,每次匹配相当于树上的一条链。完整的跑一次 AC 自动机相当于若干条链的并。

于是对于所有点 \(u\),按照 dfs 序排序后,依次做以下操作。把根到 \(u_i\) 上的点加 \(1\)。把 \(\operatorname{lca}(u_i,u_{i+1})\) 到根的路径全部减 \(1\)。

树上差分一下,用树状数组维护即可。

「JSOI2009」密码

给定 \(n\) 个模式串,求长度为 \(L\) 且包含所有模式串的文本串个数,若个数小于等于 \(42\),则按字典序升序输出全部文本串。

对于 \(100 \%\) 的数据,\(1 \le L \le 25,1 \le n \le 10\)。

可以想到 AC 自动机。先对 \(n\) 个串建 AC 自动机,然后很自然的想到状压 DP。初始化的时候每个串的最终位置打上自己,然后建 AC 自动机的时候,注意并上 \(fail_u\) 的状态。

设 \(f(i,j,s)\) 表示当前到该串的第 \(i\) 位,在 AC 自动机上走到位置 \(j\),当前字符串的出现状态为 \(s\)。转移直接转移就可以了。注意 AC 自动机只能往儿子跳不能跳父亲。

「JSOI2009」有趣的游戏

给你 \(n\) 个长为 \(l\) 的字符串 \(a_{1,2,...,n}\),字符集大小为 \(m\),每次随机在末尾添加一个字符,当当前字符串包含字符串 \(a_i\) 时结束操作,对 \(1\le i\le n\) 问 \(a_i\) 成为结束操作时所包含的字符串的概率。

其中 \(1 \le n,m,l \le 10\)。

先建出 AC 自动机,然后把 \(fail\) 树建出来,问题变成了在停在某一点的概率是多少。

考虑设概率会有小问题,所以不妨设 \(f(x)\) 表示经过 \(x\) 的期望,于是有

\[f(x) = [x=0] + \sum\limits_y \operatorname{P}(y \rightarrow x) f(y) \]

然后高斯消元一下即可。

「NOI2021」路径交点

首先注意到部分分有一个 \(k=2\) 的情况,然后发现对于第二列,相当于第一列的一个排列,交点相当于是逆序对。奇数减偶数的形式很容易联想到行列式。于是 \(k=2\) 的情况的答案就是邻接矩阵的行列式的值。

对于一般情况,不妨设 \(f_i\) 表示第 \(i\) 层到第 \(i+1\) 层交点为偶数的情况,\(g_i\) 就是奇数。于是发现 \((f_i-g_i)(f_{i+1}-g_{i+1})=f_if_{i+1}+g_ig_{i+1}-f_ig_{i+1}-g_if_{i+1}\)。这是第 \(i\) 层到第 \(i+2\) 层的答案,根据这个,考虑将各层之间进行来两两合并,合并就相当于是做矩阵乘法。于是答案为所有邻接矩阵相乘后的行列式的值。

「POI2000」病毒

已知某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在试问,是否存在一个无限长的安全的二进制代码。

对于所有数据满足 \(1 \le n \le 2000, \sum |s| \le 3 \times 10^4\)。

首先建出 AC 自动机,在一个病毒串的结尾打上标记。相当于我们要构造一个无限长的串,他在 AC 自动机上跑不会跑到标记,也就是要找到一个无标记的环。

所以直接跑,看能否直接跑到一个无标记的环上即可。

「POI2010」CHO-Hamsters

给出 \(n\) 个互不包含的字符串,要求你求出一个最短的字符串 \(S\),使得这 \(n\) 个字符串在 \(S\) 中总共至少出现 \(m\) 次,问 \(S\) 最短是多少。

\(1\le n\le 200\),\(1\le m\le 10^9\) ,所有字符串的总长 \(\le 10^5\)。

首先可以想到如果当前一个串的结尾是 \(s_i\),然后我要往上拼一个 \(s_j\),至少需要多少个字符,然后因为这 \(n\) 个串是不互相包含的,所以不会出现 \(s_j\) 出现在 \(s_i\) 中间的情况。于是 \(s_j\) 匹配 \(s_i\) 的情况显然可以 KMP 解决。

于是我们预处理出 \(d_{i,j}\) 表示当前串的末尾是 \(s_i\),然后往后拼一个 \(s_j\) 至少的字符数。考虑设 \(f(i,j)\) 表示总共出现了 \(i\) 次,结尾是 \(s_j\) 的字符串最短长度。于是有

\[f(i,j)=\min \{ f(i-1,k)+d_{k,j} \} (1 \le k \le n) \]

然后看这 \(m\) 范围可以猜到是矩阵快速幂。 注意到这个形式有点类似矩阵乘法,然后我们把矩阵乘法改成取 \(\min\)。矩阵优化即可。

复杂度 \(\mathcal{O}(n \sum |s_i| + n^3 \log m)\)。

「POI2017」Flappy Bird

在游戏中,小鸟一开始位于 \((0,0)\) 处,它的目标是飞到横坐标为 \(X\) 的某个位置上。

每一秒,你可以选择点击屏幕,那么小鸟会从 \((x,y)\) 飞到 \((x+1,y+1)\),或者不点击,那么小鸟会飞到 \((x+1,y-1)\)。

在游戏中还有 \(n\) 个障碍物,用三元组 \((x_i,a_i,b_i)\) 描述,表示在直线 \(x=x_i\) 上,\(y\le a_i\) 或者 \(y\ge b_i\) 的部分都是障碍物,碰到或者擦边都算游戏失败。

现在,请你求出小鸟从 \((0,0)\) 飞到目的地最少需要点击多少次屏幕。

对于 \(100\%\) 的数据,\(0\le n\le 500000\),\(1\le X\le10^9\),\(0<x_i<X\),\(-10^9\le a_i<b_i\le 10^9\)。

不妨先假设要从 \((0,0)\) 到 \((a,b)\)。设 \(x\) 表示上升次数,\(y\) 表示下降次数。于是可以得到 \(x= \dfrac{a+b}{2}\)。也就是说区间中能走的点必须奇偶不变。

每次转移的时候处理能走过的区间,如果和柱子中间有交则可以通过。

注意在数据有负数的情况下,ans+=(x%2);ans+=(!(x%2==0)); 是不等价的。

「SP8222」NSUBSTR - Substrings

给定一个字符串 \(S\),定义 \(F(x)\) 为 \(S\) 的某些长度为 \(x\) 的子串在 \(S\) 中的最大出现次数。对于每个 \(1\le i \le |S|\) 输出 \(F(i)\)。

对于所有数据,满足 \(|S| \le 250000\)。

首先 \(F(i)\) 的答案为长度为 \(i\) 的字串的 \(|\operatorname{endpos}|\) 最大值。于是考虑在 parent 树上 DP,DP 出每一个点的 \(|\operatorname{endpos}|\) 大小。

\[f(u) \leftarrow f(u)+f(v) \]

然后从后到前取 \(\max\) 即可。

「TJOI2013」单词

给定 \(n\) 个字符串,问每个字符串在这些字符串中出现的个数。

对于 \(100 \%\) 数据,\(1 \le n \le 200, \sum |s_i| \le 10^6\)。

注意到 \(fail_u\) 是当前点失配后所指向的点。所以可以考虑把 \(fail\) 树建出来。对每个点设权值,表示这个点属于多少个串,然后根据 \(fail_u\) 加上 \(u\) 的权值。

于是对于代表完整的 \(n\) 个串的权值就是他出现的次数了。

「TJOI2015」弦论

对于一个给定的长度为 \(n\) 的字符串,求出它的第 \(k\) 小子串是什么。

其中 \(n \le 5 \times 10^5,k \le 10^9\)。

建出 SAM,然后计算出每个点重复的次数,直接 parent 树上跑一遍。然后如果没有本质不同,直接对 SAM 上做个前缀和然后直接跑。如果本质不同算一个,那把相同的直接设为 \(1\) 再跑即可。

「USACO15FEB」Censoring G

给你一个文本串 \(s\),和一大堆模式串 \(t_i\),现在进行如下操作:

  • 找到出现位置最靠前的模式串。
  • 删掉它。

对于所有数据,保证 \(1 \le |s|, \sum t_i ,n \le 10^5\)。且没有任何一个字符串是另一个字符串的子串。

建出 AC 自动机,然后跑 \(s\),如果跑到第一个模式串就把他删掉,删掉操作相当于回到第一个匹配这个串的点上。用一个栈记录一下即可。

SvT

给一个字符串,每次询问给出若干个他的 \(t\) 个后缀,求这些后缀两两之间的 LCP 的长度之和。

其中 \(|S| \le 5 \times 10^5, \sum t \le 3 \times 10^6\)。

看到 LCP 首先想到 SA。于是可以枚举后缀然后用 \(height\) 数组求 LCP,复杂度 \(\mathcal{O}(n^2 \log n)\)。

考虑如何优化,对于区间 \(\min\) 可以联想到笛卡尔树,于是我们把 \(height\) 数组建一个笛卡尔树,然后对于每个点算他的贡献,即他作为 \(\operatorname{lca}\) 的次数。

于是一个点的贡献即为他左子树有贡献点的个数加上自身贡献的数量,乘上右子树有贡献点的个数加上自身贡献的数量。

跑一遍 dfs 即可。复杂度 \(\mathcal{O}(n \log n)\)。

「2022-07-25 省队集训 - Day 1」

Problem A

首先考虑 “口” 形怎么办,发现当四条边如果是一样的,无论先手怎么操作,后手可以通过相应的操作,使最后的四条边依然相等。于是最后当四条边为 \(0\) 的时候,先手必败。

然后考虑 “日” 形,把他先看成两个点三条边,因为不可能出现选 \(2\) 条及以上的情况,于是变成了 Nim 游戏,三条边异或和如果为 \(0\) 则先手必败。

结合一下口的结论,如果日的最上面三条边或最下面三条边没有相同,先手可以通过一次操作把他变成相同,然后就变成两点三边的情况了。于是先手必败的条件为上面三条边相等,下面三条边相等,且上面的一条边,中间的边,下面的一条边异或和为 \(0\)。

Problem B

阶梯 Nim 板子。我的远古的一篇学习笔记有写,复制过来然后修改一下格式(

有 \(n\) 个位置 \(1 \sim n\),每个位置上有 \(a_i\) 个石子。

有两个人轮流操作。操作步骤是:挑选 \(1 \sim n\) 中任一一个存在石子的位置 \(i\),将至少 \(1\) 个石子移动至 \(i−1\) 位置(也就是最后所有石子都堆在在 \(0\) 这个位置)。谁不能操作谁输。求先手必胜还是必败。

其实这个结果只跟奇数阶梯上的石子有关。

如果先手把偶数阶梯的若干石子往下移,那么后手也会相应把这堆石子继续往下移,直到 \(0\),然鹅这样并不会改变什么,只会改变石子的数量。

如果先手吧奇数阶梯的若干石子往下移,那么当这堆石子到 \(0\) 的位置时,变成之前的后手先移其他的了。

所以,这题只要把奇数阶梯上的石子走一遍 Nim 即可。

Problem C

对于一个数 \(x\) 来说,如果最后贡献 \(1\) 的是他,那么他不能被删掉。加入我 \(y\) 个 \(x\),保留最多 \(x\) 的方法就是每次对半分,然后就会剩下一半的 \(x-1\)。

所以一个数如果有贡献,那么可以把 \(2^{x-1}\) 个 \(x\) 替换 \(2^{x-2}\) 个 \(x-1\)。

判断一下 \(2^{-i} \cdot \sum \left\lfloor \frac{a_i}{2} \right\rfloor \le 1\)。注意后面对前面贡献。

好像反过来更好写?

Problem D

首先可以直观感受到,先手如果某一堆选了 \(x\),后手可以在同样一堆选择 \(y\)。所以先对每一堆取膜 \((x+y)\)。这样一堆就不能同时被 \(x,y\) 选了。

然后现在分类讨论,不妨设 \(x<y\),一堆的数量 \(a\) 可能 \(1 \le a_i < x\) 或 \(x \le a_i <y\) 或 \(y \le a_i < x+y\)。

第一种显然是没用了。第二种只有 \(x\) 可以取的,第三种是都可以取,所以如果第一堆存在,\(x\) 必胜。注意还有一种形如 \(y \le 2x \le a_i < x+y\) 的情况,这种可以取 \(x\) 多次,但不能 \(x,y\) 同时取,和第二种情况是等价的。

分类讨论即可。

Problem E

首先通过乱搞乱画得知答案是 \(2^n\)。 首先证明答案的上界是 \(2^n\),这个比较显然,因为有 \(2n\) 列,黑色有 \(n\) 个白色有 \(n\) 个,直接把前 \(n\) 列全部的情况的反过来填补后 \(n\) 个即可,即 \(2^n\) 种。

尝试证明下界。考虑 Alice 的策略,贪心,考虑一个黑色点落在哪里比较好。显然是在已知上面已有的行中的该列出现的白色点最多。

因为只有两种颜色,根据抽屉原理,该列下来出现的白色数量一定超过一半。所以到 \(2^n\) 都可以有不同的,于是下界是 \(2^n\)。于是答案就是 \(2^n\)。

Problem F

可以按照蛇字形填数,如果是偶数列,可以发现刚刚好满足条件。

如果奇数列,最后会剩下一列会有相差,考虑把前 2 列移出来,然后通过调整使其满足条件。

Problem G

数竞题。Orz。

首先按照 \(a_i\) 排序,然后取走 \(a_i\) 最大的那个;再按 \(b_i\) 排序,取走 \(b_i\) 最大的。偶数情况一定比奇数情况更强,因为奇数可以多取,所以只考虑偶数情况。

然后考虑怎么取,此时剩下 \(2m-2\) 个,按照 \(a_i\) 排序,相邻两个看成一组,如果对于所有组都恰好取一个,结果一定大于一半,因为最差的情况就是取每组最小的,再加上之前取的最大的,结果依然大于一半。

于是对 \(a_i\) 排序,相邻两个连边;再把 \(b_i\) 也这么做一遍。然后连出来若干个偶环,每个偶环跳着选即可。



这篇关于「2022/07」学习记录的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程