杂题选做 [S-C1]

【S-C1】是啥意思??

01. CF1858E2 Rollbacks (Hard Version)

维护一个初始为空的序列 a a a,支持以下操作:

  • +   x \texttt{+ }x x:在序列末端插入 x x x
  • -   k \texttt{- }k k:在序列末端删除 k k k 个数( k k k 不超过当前序列长度);
  • ? \texttt{?} ?:查询序列中不同的数字个数;
  • ! \texttt{!} !:撤回前一个 + / - \texttt +/\texttt - +/- 操作。

q q q 次操作,强制在线, 1 ≤ q , x ≤ 10 6 1\le q,x\le 10^6 1q,x106,询问操作不超过 10 5 10^5 105

解法

垃圾 *2600。

经典地,我们用 set 维护所有数字的出现的所有位置,更新时只需要查询其首位即可。

这样我们可以轻松实现 + \texttt + + 操作。

为了实现 - \texttt - - 操作,我们引入序列 A A A,使得序列 A A A 的前 l l l 位恰好为序列 a a a l l l 为序列 a a a 此时的长度)。这样一来,我们更新时直接将 l l l 更改为 l − k l-k lk 即可。

这样做同样是为了方便进行 ! \texttt ! ! 操作。因为我们实际上保留了 1 ∼ l max ⁡ 1\sim l_{\max} 1lmax 的所有信息,足以进行回溯。

对于 ! \texttt ! ! 操作,我们使用 stack 存储所有操作,并尝试逆向地进行还原,具体情况与上面的 + / - \texttt +/\texttt - +/- 操作思路一致。

特别地,对于 + \texttt + + 操作,我们需要存储原来的 A l ′ A_{l'} Al 而不是 x x x。这是容易理解的。

至于答案的更新,我们在所有元素第一次出现的位置标记为 1 1 1,这个是已经维护过的。

那么 ? \texttt ? ? 操作只需要输出该数组在 [ 1 , l ] [1,l] [1,l] 上的区间和即可。

至此,我们需要一个可以支持单点修改、查询区间和的数据结构。拉一个树状数组即可。

时间复杂度 O ( q log ⁡ q ) O(q\log q) O(qlogq)

https://codeforces.com/contest/1858/submission/219045104

考虑优化,逐个解决瓶颈:

  • 树状数组:可以用前缀和替代。容易发现是可行的;
  • 维护各个元素出现的位置:我们发现改变一个元素第一次出现的位置,必然伴随着一个当前已知位置该元素的更新或该元素的彻底删除。据此可以只用一个数组 p o s pos pos 代替上述题解中提出的 set 进行维护。

据此,我们得到时间复杂度 O ( q ) O(q) O(q) 的做法。代码从略。

02. CF1804F Approximate Diameter

给定一个 n n n 个点 m m m 条边的初始无向图,有 q q q 次更新,每次更新向图中添加一条边。设 d ( G i ) d(G_i) d(Gi) 代表加入 i i i 条边后的图中两点之间的最大距离,你需要输出 q + 1 q+1 q+1 个整数 a 0 , a 1 , … , a q a_0,a_1,\dots,a_q a0,a1,,aq,满足 ⌈ d ( G i ) 2 ⌉ ≤ a i ≤ 2 ⋅ d ( G i ) \Bigl\lceil\dfrac{d(G_i)}{2}\Bigr\rceil\le a_i\le 2\cdot d(G_i) 2d(Gi)ai2d(Gi)

n , m , q ≤ 10 5 n,m,q\le 10^5 n,m,q105,图连通。

解法

牛逼 *2700。

考虑一个性质,以某个点为端点的最长路径长度 s s s 满足

⌈ d ( G i ) 2 ⌉ ≤ s ≤ d ( G i ) \Bigl\lceil\dfrac{d(G_i)}2\Bigr\rceil\le s\le d(G_i) 2d(Gi)sd(Gi)

证明从略。

但是发现这个比要求的更紧。考虑怎么利用这个 2 ⋅ d ( G i ) 2\cdot d(G_i) 2d(Gi)

显然地,我们考虑二分某个答案什么时候失效,此时我们将该答案除以 2 2 2 即可。这是可行的,因为这个一眼单调。

二分枚举端点再套枚举答案即可,时间复杂度 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)

https://codeforces.com/contest/1804/submission/219393324

注意我们钦点的 dijkstra 算法别写丑,不然无法通过。

03. CF992E Nastya and King-Shamans

给定序列 a a a,维护两种操作:

  • 单点将 a i → x a_i\to x aix
  • 查询 ∑ i = 1 n [ ( ∑ j = 1 i − 1 a j ) = a i ] \sum_{i=1}^n\Bigl[\Bigl(\sum_{j=1}^{i-1}a_j\Bigr)=a_i\Bigr] i=1n[(j=1i1aj)=ai]

1 ≤ n , q ≤ 2 × 10 5 1\le n,q\le 2\times10^5 1n,q2×105 0 ≤ a ≤ 10 9 0\le a\le 10^9 0a109

解法

还不错的 *2500。

考虑一个性质:答案最多为 log ⁡ n \log n logn

证明从略。我们考虑直接拉线段树维护然后暴力遍历即可找到答案。

时间复杂度 O ( q log ⁡ 2 n ) O(q\log^2 n) O(qlog2n)

https://codeforces.com/contest/992/submission/219414380

04. CF825G Tree Queries

一棵树有 n n n 个节点,初始均为白色,有两种操作:

  • 1   x \texttt{1 } x x:把节点 x x x 染为黑色;
  • 2   x \texttt{2 } x x:查询 x x x 到树上任意一个黑色节点的简单路径上的编号最小的节点的编号。

保证第一个操作为染色操作,强制在线。 3 ≤ n ≤ 10 6 3\le n\le 10^6 3n106 1 ≤ q ≤ 10 6 1\le q\le 10^6 1q106

解法

小清新 *2500!

首先,显然地,为了方便维护,我们以第一次染色的节点 t t t 为根 dfs 一遍以求出所有节点到 t t t 的简单路径上的编号最小的节点的编号,即数组 a a a

然后画个图可以理解:

  • 对于染色操作,将目前维护的一个答案 a n s ← min ⁡ ( a n s , a x ) ans\gets \min(ans,a_x) ansmin(ans,ax),可以证明这是正确的;
  • 对于询问操作,我们输出 min ⁡ ( a n s , a x ) \min(ans,a_x) min(ans,ax) 即可。

这是因为,我们的对于不同 t t t 的子树间的路径 u → v u\to v uv,有 u → t → v u\to t\to v utv 即为其简单路径。而对于子树内的路径,可以用 u → t , v → t u\to t,v\to t ut,vt 覆盖整个路径。

至此问题得到 O ( n ) O(n) O(n) 解决。

https://codeforces.com/contest/825/submission/219512635

05. CF1503D Flip the Cards

你有 n n n 张牌,第 i i i 张牌的正面有整数 a i a_i ai,背面有整数 b i b_i bi。所有 1 1 1 2 n 2n 2n 的整数都出现过正好一次。

我们认为这 n n n 张牌是好的当且仅当 a i a_i ai 升序, b i b_i bi 降序。

可以进行下面的操作:

  • 交换 a i , b i a_i,b_i ai,bi
  • 随意重排这 n n n 张牌。

求如果要使这 n n n 张牌变成好的最少需要翻几次牌,或报告无解。

解法

牛逼 *2600。可惜放了 log ⁡ \log log 过。

我们考虑到,若存在一个 i i i 使得 a i , b i ≤ n a_i,b_i\le n ai,bin,则必然无解。

于是考虑将所有牌变成 a i < b i a_i<b_i ai<bi 的情况,则必然有 a i ≤ n , b i > n a_i\le n,b_i>n ain,bi>n。于是设 f ( a i ) = b i f(a_i)=b_i f(ai)=bi

下面研究 f ( i ) f(i) f(i)。发现当且仅当 f ( 1 … n ) f(1\ldots n) f(1n) 可以被分为两个单调递减子序列使有解。

min ⁡ j = 1 i { a j } > max ⁡ j = i + 1 n { a j } \min_{j=1}^i\{a_j\}>\max_{j=i+1}^n\{a_j\} minj=1i{aj}>maxj=i+1n{aj} 处断点分段处理最小值即可。

时间复杂度 O ( n ) O(n) O(n),远超 log ⁡ \log log 且更巧妙。

同时注意到没必要管顺序。

https://codeforces.com/contest/1503/submission/219536981

06. CF1646F Playing Around the Table

n n n 个人坐成一个圈,每个人手上有 n n n 张麻将,麻将上是 1 1 1 萬到 n n n 萬,每次可以让每个人同时掏出一张麻将给下一个人,构造一组方案使得第 i i i 个人能够做成「 i i i 萬一色」。

要求,操作次数不超过 n 2 − n n^2-n n2n 1 ≤ n ≤ 100 1\le n\le 100 1n100

解法

萌萌 *2900。

我们考虑到,为了方便处理,设计一个过渡态「一气通贯」使所有情况达到统一。

可以证明,初态 → \to 「一气通贯」 → \to i i i 萬一色」这一变换中,第一步的操作次数不超过 n ( n − 1 ) / 2 n(n-1)/2 n(n1)/2,第二步的操作次数为 n ( n − 1 ) / 2 n(n-1)/2 n(n1)/2

下面,我们证明第一个。发现离「一气通贯」最远的状态是每个人均有一个「 j j j 萬一色」,证毕。

https://codeforces.com/contest/1646/submission/219623330

感觉还是比较难想的,所以场上才仅 3 人通过。

07. CF802H Fake News (medium)

构造字符串 s , p s,p s,p,使字符串 s s s 的为 p p p 的子串恰有 n   ( 1 ≤ n ≤ 10 6 ) n\ (1\le n\le 10^6) n (1n106) 个。

解法

萌萌 *2200!感觉思路比较牛逼。

我首先想到了 CF1368B Codeforces Subsequences,不同的是,这道题是至少 n n n 个。

一个朴素的想法是,对于恰好有 k k k 个子串的情况 ( s , p ) (s,p) (s,p),我们可以实现 k → 2 k k\to 2 k k2k

具体地,任取一个新字符 c c c,令 s ′ = s c c s'=scc s=scc p ′ = p c p'=pc p=pc 即可(这里直接用字符串拼接表示了)。

但是我们不能实现 k → k + 1 k\to k+1 kk+1。这是很困难的。否则我们可以直接二进制拆分 n n n 解决。

考虑另一个思路,假设对于恰好有 k k k 个子串的情况 ( s , p ) (s,p) (s,p) s s s 可以被表示为 p u pu pu u u u 也是一个字符串),任取一个新字符 c c c,那么我们可以实现

  • k → 2 k + 1 k\to 2 k+1 k2k+1:令 s ′ = p c u c c s'=pcucc s=pcucc p ′ = p c p'=pc p=pc 即可;
  • k → 2 k + 2 k\to 2 k+2 k2k+2:令 s ′ = p c c u c c s'=pccucc s=pccucc p ′ = p c p'=pc p=pc 即可。

这之后我们均直接可以更新 u ′ u' u

于是我们递归地解决这个问题即可。考虑每次处理 x x x 时,先解决 ⌊ x − 1 2 ⌋ \left\lfloor\frac{x-1}2\right\rfloor 2x1 时的情况,再回溯解决。容易发现这是合理的。

边界是 x = 1 , 2 x=1,2 x=1,2。这个也是平凡的,只需要使 p = a p=\texttt a p=a 即可。

这个是很对的,不考虑 string 类的复杂度是 O ( log ⁡ n ) O(\log n) O(logn)。由于 2 26 > 10 6 2^{26}>10^6 226>106,我们可以仅用小写字母解决本题。

https://codeforces.com/contest/802/submission/219735685

08. CF1363F Rotating Substrings

给定两个长度为 n n n 的字符串 s , t s,t s,t。定义一次操作为选择 s s s 的一个子串 s l , l + 1 , … , r s_{l, l +1, \dots, r} sl,l+1,,r,然后将其修改为 s r , l , l + 1 , l + 2 , … , r − 1 s_{r, l, l + 1, l + 2, \dots, r - 1 } sr,l,l+1,l+2,,r1。请求出使 s s s t t t 相等的最小操作次数或判定无解。

多组数据, ∑ n ≤ 2000 \sum n \leq 2000 n2000 s , t s, t s,t 中只有小写字母。

解法

很好 *2600。

我们考虑设 d p i , j dp_{i,j} dpi,j 表示 s s s 长度为 i i i 的前缀插入 i i i 后的若干个字符后,等于 t t t 长度为 j j j 的前缀的最小花费。显然这里有 i ≤ j i\le j ij。我们分情况转移:

  1. 如果 s i = t j s_i=t_j si=tj,可以直接匹配,也即 d p i , j = d p i − 1 , j − 1 dp_{i,j}=dp_{i-1,j-1} dpi,j=dpi1,j1
  2. 如果 s r + 1 , n s_{r+1,n} sr+1,n 中存在 t j t_j tj,我们可以直接拉过来匹配,也即 d p i , j = d p i , j − 1 dp_{i,j}=dp_{i,j-1} dpi,j=dpi,j1我们这里暂时不计算贡献,具体原因见下
  3. 直接把 s i s_i si 拉到前面去,也即 d p i , j = d p i − 1 , j + 1 dp_{i,j}=dp_{i-1,j}+1 dpi,j=dpi1,j+1。这样的目的在于,我们保证了 2 中必定将会有某个 t j t_j tj 被拉到前面去并计算了其贡献。

于是就可以转移了。

感觉这个假借字符的思路比较巧妙。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值