题解 P8932【[JRKSJ R7] Clock Paradox】
题意
给你一个字符串 $S$,设 $S=\overline{s_1s_2\dots s_n}$。
有一个字符串 $T$,初始时 $T=S$,你可以进行若干次操作,每次操作可以选取 $S$ 一个子串并插入到 $T$ 的任意位置。
你希望经过若干次操作后,$T=\overline{s_1s_1s_2s_2\dots s_ns_n}$,定义 $f(S)$ 为满足此条件所需的最少的操作次数。
此外,字符串 $S$ 还会发生一些改变。具体地,有 $q$ 次修改操作,每次修改操作会给出 $p$ 和 $\texttt{c}$,表示令 $s_p\gets \texttt{c}$。
你需要在最开始和每次修改后求出 $f(S)$ 的值。
$|S|,q\le3\times10^6$。
思路
设 $S$ 的颜色段数为 $x$,一眼看上去,我们可以看出 $f(S)=\lceil\dfrac{x}{2}\rceil$,考虑证明,使用数学归纳法。
对于长度为 $1$ 的字符串,其显然成立。
向 $S$ 后面加入一个字符 $c$,令其为第 $i$ 位。
若 $c$ 等于 $S$ 的最后一个字符,则将进行的最靠右的操作后再加上一个 $c$ 即可。
如 $\texttt{abbbab}$ 最靠右的操作为选取 $\texttt{ab}$ 插入到 $\texttt{a\_b}$ 之间。则 $\texttt{abbbabb}$ 最靠右的操作为选取 $\texttt{abb}$ 插入到 $\texttt{a\_bb}$ 之间。
此类情况答案不变。
否则,若 $S$ 的最靠右的操作只含一种字符,则将进行的最靠右的操作后再加上一个 $c$ 并置于字符串末尾即可。
如 $\texttt{abccc}$ 最靠右的操作为选取 $\texttt{ccc}$ 添加到 $\texttt{ab\_ccc}$ 之间。则 $\texttt{abcccd}$ 最靠右的操作为选取 $\texttt{cccd}$ 插入到 $\texttt{ccc\_d}$ 之间。
此类情况答案不变。
否则,只能选取字符 $c$ 并插入到字符串末尾作为最新的最右端操作。
如 $\texttt{abcd}$ 最靠右的操作为选取 $\texttt{cd}$ 添加到 $\texttt{c\_d}$ 之间。则 $\texttt{abcde}$ 最靠右的操作为选取 $\texttt{e}$ 插入到 $\texttt{e}$ 后面。
如果不这样操作,则 $c$ 被加到之前的操作中,则 $T$ 的最后两个字符为 $S_{i-1}S_i$,不合题意。
此类情况答案加一。
其操作的实质是选取相邻的两个颜色段并将其插入到两段的交点间。
接下来,问题变成了单点修改,全局颜色段数。颜色段数转化为 $S_i\ne S_{i+1}$ 的位置 $i$ 的数量。
考虑修改一个位置,分类讨论新的值与两边的数的关系,旧的值与两边的数的关系,动态维护 $x$ 就好了,可以做到 $O(1)$。
时间复杂度 $O(|S|+q)$。
代码(仅供参考):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
}
const int N=3e6+10;
int n,q,x;
char s[N];
int main(){
q=read();
n=readstr(s);
for(int i=1,j=1;i<=n;i=j+1){
while(j+1<=n&&s[j+1]==s[i]) j++;
x++;
}
write(x+1>>1),putc('\n');
while(q--){
int a=read();
char c=getc();
if(s[a-1]==s[a]&&s[a-1]!=c)
x++;
if(s[a+1]==s[a]&&s[a+1]!=c)
x++;
if(s[a-1]!=s[a]&&s[a-1]==c)
x--;
if(s[a+1]!=s[a]&&s[a+1]==c)
x--;
s[a]=c;
write(x+1>>1),putc('\n');
}
flush();
}
再见 qwq~

浙公网安备 33010602011771号