杂题选做 [S-C1]
CSP_Sept
·
·
个人记录
【S-C1】是啥意思??
01. CF1858E2 Rollbacks (Hard Version)
维护一个初始为空的序列 a,支持以下操作:
有 q 次操作,强制在线,1\le q,x\le 10^6,询问操作不超过 10^5。
解法
垃圾 *2600。
经典地,我们用 set 维护所有数字的出现的所有位置,更新时只需要查询其首位即可。
这样我们可以轻松实现 \texttt + 操作。
为了实现 \texttt - 操作,我们引入序列 A,使得序列 A 的前 l 位恰好为序列 a(l 为序列 a 此时的长度)。这样一来,我们更新时直接将 l 更改为 l-k 即可。
这样做同样是为了方便进行 \texttt ! 操作。因为我们实际上保留了 1\sim l_{\max} 的所有信息,足以进行回溯。
对于 \texttt ! 操作,我们使用 stack 存储所有操作,并尝试逆向地进行还原,具体情况与上面的 \texttt +/\texttt - 操作思路一致。
特别地,对于 \texttt + 操作,我们需要存储原来的 A_{l'} 而不是 x。这是容易理解的。
至于答案的更新,我们在所有元素第一次出现的位置标记为 1,这个是已经维护过的。
那么 \texttt ? 操作只需要输出该数组在 [1,l] 上的区间和即可。
至此,我们需要一个可以支持单点修改、查询区间和的数据结构。拉一个树状数组即可。
时间复杂度 O(q\log q)。
https://codeforces.com/contest/1858/submission/219045104
考虑优化,逐个解决瓶颈:
- 树状数组:可以用前缀和替代。容易发现是可行的;
- 维护各个元素出现的位置:我们发现改变一个元素第一次出现的位置,必然伴随着一个当前已知位置该元素的更新或该元素的彻底删除。据此可以只用一个数组 pos 代替上述题解中提出的 set 进行维护。
据此,我们得到时间复杂度 O(q) 的做法。代码从略。
02. CF1804F Approximate Diameter
给定一个 n 个点 m 条边的初始无向图,有 q 次更新,每次更新向图中添加一条边。设 d(G_i) 代表加入 i 条边后的图中两点之间的最大距离,你需要输出 q+1 个整数 a_0,a_1,\dots,a_q,满足 \Bigl\lceil\dfrac{d(G_i)}{2}\Bigr\rceil\le a_i\le 2\cdot d(G_i)。
### 解法
牛逼 \*2700。
考虑一个性质,以某个点为端点的最长路径长度 $s$ 满足
$$
\Bigl\lceil\dfrac{d(G_i)}2\Bigr\rceil\le s\le d(G_i)
$$
证明从略。
但是发现这个比要求的更紧。考虑怎么利用这个 $2\cdot d(G_i)$。
显然地,我们考虑二分某个答案什么时候失效,此时我们将该答案除以 $2$ 即可。这是可行的,因为这个一眼单调。
二分枚举端点再套枚举答案即可,时间复杂度 $O(n\log^2 n)$。
<https://codeforces.com/contest/1804/submission/219393324>
注意我们钦点的 dijkstra 算法别写丑,不然无法通过。
## _03._ CF992E Nastya and King-Shamans
给定序列 $a$,维护两种操作:
- 单点将 $a_i\to x$;
- 查询 $\sum_{i=1}^n\Bigl[\Bigl(\sum_{j=1}^{i-1}a_j\Bigr)=a_i\Bigr]$。
$1\le n,q\le 2\times10^5$,$0\le a\le 10^9$。
### 解法
还不错的 \*2500。
考虑一个性质:答案最多为 $\log n$。
证明从略。我们考虑直接拉线段树维护然后暴力遍历即可找到答案。
时间复杂度 $O(q\log^2 n)$。
<https://codeforces.com/contest/992/submission/219414380>
## _04._ CF825G Tree Queries
一棵树有 $n$ 个节点,初始均为白色,有两种操作:
- $\texttt{1 } x$:把节点 $x$ 染为黑色;
- $\texttt{2 } x$:查询 $x$ 到树上任意一个黑色节点的简单路径上的编号最小的节点的编号。
保证第一个操作为染色操作,强制在线。$3\le n\le 10^6$,$1\le q\le 10^6$。
### 解法
小清新 \*2500!
首先,显然地,为了方便维护,我们以第一次染色的节点 $t$ 为根 dfs 一遍以求出所有节点到 $t$ 的简单路径上的编号最小的节点的编号,即数组 $a$。
然后画个图可以理解:
- 对于染色操作,将目前维护的一个答案 $ans\gets \min(ans,a_x)$,可以证明这是正确的;
- 对于询问操作,我们输出 $\min(ans,a_x)$ 即可。
这是因为,我们的对于不同 $t$ 的子树间的路径 $u\to v$,有 $u\to t\to v$ 即为其简单路径。而对于子树内的路径,可以用 $u\to t,v\to t$ 覆盖整个路径。
至此问题得到 $O(n)$ 解决。
<https://codeforces.com/contest/825/submission/219512635>
## _05._ CF1503D Flip the Cards
你有 $n$ 张牌,第 $i$ 张牌的正面有整数 $a_i$,背面有整数 $b_i$。所有 $1$ 到 $2n$ 的整数都出现过正好一次。
我们认为这 $n$ 张牌是好的当且仅当 $a_i$ 升序,$b_i$ 降序。
可以进行下面的操作:
- 交换 $a_i,b_i$。
- 随意重排这 $n$ 张牌。
求如果要使这 $n$ 张牌变成好的最少需要**翻几次牌**,或报告无解。
### 解法
牛逼 \*2600。可惜放了 $\log$ 过。
我们考虑到,若存在一个 $i$ 使得 $a_i,b_i\le n$,则必然无解。
于是考虑将所有牌变成 $a_i<b_i$ 的情况,则必然有 $a_i\le n,b_i>n$。于是设 $f(a_i)=b_i$。
下面研究 $f(i)$。发现当且仅当 $f(1\ldots n)$ 可以被分为两个单调递减子序列使有解。
在 $\min_{j=1}^i\{a_j\}>\max_{j=i+1}^n\{a_j\}$ 处断点分段处理最小值即可。
时间复杂度 $O(n)$,远超 $\log$ 且更巧妙。
同时注意到没必要管顺序。
<https://codeforces.com/contest/1503/submission/219536981>
## _06._ CF1646F Playing Around the Table
有 $n$ 个人坐成一个圈,每个人手上有 $n$ 张麻将,麻将上是 $1$ 萬到 $n$ 萬,每次可以让每个人同时掏出一张麻将给下一个人,构造一组方案使得第 $i$ 个人能够做成「$i$ 萬一色」。
要求,操作次数不超过 $n^2-n$;$1\le n\le 100$。
### 解法
萌萌 \*2900。
我们考虑到,为了方便处理,设计一个过渡态「一气通贯」使所有情况达到统一。
可以证明,初态 $\to$「一气通贯」$\to$「$i$ 萬一色」这一变换中,第一步的操作次数不超过 $n(n-1)/2$,第二步的操作次数为 $n(n-1)/2$。
下面,我们证明第一个。发现离「一气通贯」最远的状态是每个人均有一个「$j$ 萬一色」,证毕。
<https://codeforces.com/contest/1646/submission/219623330>
感觉还是比较难想的,所以场上才仅 3 人通过。
## _07._ CF802H Fake News (medium)
构造字符串 $s,p$,使字符串 $s$ 的为 $p$ 的子串恰有 $n\ (1\le n\le 10^6)$ 个。
### 解法
萌萌 \*2200!感觉思路比较牛逼。
我首先想到了 [CF1368B Codeforces Subsequences](https://codeforces.com/contest/1368/problem/B),不同的是,这道题是**至少** $n$ 个。
一个朴素的想法是,对于恰好有 $k$ 个子串的情况 $(s,p)$,我们可以实现 $k\to 2 k$。
具体地,任取一个**新字符** $c$,令 $s'=scc$,$p'=pc$ 即可(这里直接用字符串拼接表示了)。
但是我们不能实现 $k\to k+1$。这是很困难的。否则我们可以直接二进制拆分 $n$ 解决。
考虑另一个思路,假设对于恰好有 $k$ 个子串的情况 $(s,p)$,$s$ 可以被表示为 $pu$($u$ 也是一个字符串),任取一个新字符 $c$,那么我们可以实现
- $k\to 2 k+1$:令 $s'=pcucc$,$p'=pc$ 即可;
- $k\to 2 k+2$:令 $s'=pccucc$,$p'=pc$ 即可。
这之后我们均直接可以更新 $u'$。
于是我们递归地解决这个问题即可。考虑每次处理 $x$ 时,先解决 $\left\lfloor\frac{x-1}2\right\rfloor$ 时的情况,再回溯解决。容易发现这是合理的。
边界是 $x=1,2$。这个也是平凡的,只需要使 $p=\texttt a$ 即可。
这个是很对的,不考虑 string 类的复杂度是 $O(\log n)$。由于 $2^{26}>10^6$,我们可以仅用小写字母解决本题。
<https://codeforces.com/contest/802/submission/219735685>
## _08._ CF1363F Rotating Substrings
给定两个长度为 $n$ 的字符串 $s,t$。定义一次操作为选择 $s$ 的一个子串 $s_{l, l +1, \dots, r}$,然后将其修改为 $s_{r, l, l + 1, l + 2, \dots, r - 1 }$。请求出使 $s$ 与 $t$ 相等的最小操作次数或判定无解。
多组数据,$\sum n \leq 2000$,$s, t$ 中只有小写字母。
### 解法
很好 \*2600。
我们考虑设 $dp_{i,j}$ 表示 $s$ 长度为 $i$ 的前缀插入 $i$ 后的**若干**个字符后,等于 $t$ 长度为 $j$ 的前缀的最小花费。显然这里有 $i\le j$。我们分情况转移:
1. 如果 $s_i=t_j$,可以直接匹配,也即 $dp_{i,j}=dp_{i-1,j-1}$;
2. 如果 $s_{r+1,n}$ 中存在 $t_j$,我们可以直接拉过来匹配,也即 $dp_{i,j}=dp_{i,j-1}$。**我们这里暂时不计算贡献,具体原因见下**;
3. 直接把 $s_i$ 拉到前面去,也即 $dp_{i,j}=dp_{i-1,j}+1$。这样的目的在于,我们保证了 2 中必定将会有某个 $t_j$ 被拉到前面去并计算了其贡献。
于是就可以转移了。
感觉这个**假借字符**的思路比较巧妙。