P8932 [JRKSJ R7] Clock Paradox 题解

在洛谷上阅读

Part 0 题意简述

原题这场月赛我唯一AC的题

给出一个字符串 \(S\),令 \(T=S\),求使用 \(S\) 的子串插入 \(T\),将 \(T\) 变形的最少的操作次数。且字符串 \(S\) 会被修改 \(q\) 次。

具体变形:
\(S=\overline{s_1s_2…s_n}\),经过变形,使 \(T=\overline{s_1s_1s_2s_2…s_ns_n}\)

看不懂?去看样例解释。

Part 1 题目分析

那么看过样例,我们可以知道:

对于 \(S=T=\texttt{aabc}\),我们可以分别插入子串 \(\texttt{aa}\)\(\texttt{bc}\) 或分别插入子串 \(\texttt{aab}\)\(\texttt{c}\),而这两种操作所用次数相同。

那么分析这两种操作的共同特点,我们可以得出每次最多可以插入的子串特征:由两段同种字符构成的字符串组成。比如,\(\texttt{aabb}\)\(\texttt{ba}\) 就是这类子串,而 \(\texttt{aba}\)\(\texttt{abc}\) 则不是。

而这种子串的数量,就是我们要的答案。

那么根据这种子串的特征,它又可以分成两段同种字符构成的字符串。所以,我们只需要算出由同种字符构成的子串数量 \(cnt\),次数 \(ans=\lceil\dfrac{cnt}{2}\rceil\)

Part 2 代码

根据上方分析,可以写出计算次数函数:

int calc(string s){
    int cnt=1;
    for(int i=1;i<s.length();i++)
        if(s[i-1]!=s[i])
            cnt++;
    return cnt/2+cnt%2; // 这里等同于 ceil(cnt/2.0)
}

结合其他部分,写出完整代码:

#include<iostream>
#define reg register
using namespace std;
int q;
string s;
int p;
char c;
inline int calc(string s){
    reg int cnt=1;
    for(reg int i=1;i<s.length();i++)
        if(s[i-1]!=s[i])
            cnt++;
    return cnt/2+cnt%2;
}
int main(){
    ios::sync_with_stdio(0);
    cin>>q>>s;
    cout<<calc(s)<<endl;
    while(q--){
        cin>>p>>c;
        s[p-1]=c;
        cout<<calc(s)<<endl;
    }
    return 0;
}

但是,如果你把这段代码提交上去,你只能得到 50 分。为什么?

因为这段代码的时间复杂度高达 \(O(\vert S\vert\cdot q)\),你只能通过前 3 个 Subtask。

Part 3 优化

仔细观察上一段代码你就会发现它做了很多没用的事情。
在修改字符串时,子串数量 \(cnt\) 只与 \(s_{p-1}\)\(s_p\)\(s_{p+1}\)\(c\) 有关。所以我们在每次修改后只需要维护上一次的 \(cnt\) 即可。

这里的维护代码:

cnt=cnt-((int)(s[p-2]!=s[p-1])+(int)(s[p-1]!=s[p]))+((int)(s[p-2]!=c)+(int)(c!=s[p]));

原理就是将原来函数内的循环展开,这里不过多解释,请自行思考。也可以修改函数使它支持单点维护但是我懒得写

最终代码:

#include<iostream>
#define reg register
using namespace std;
int q;
string s;
int p;
char c;
int count(string s){
    reg int cnt=1;
    for(reg int i=1;i<s.length();i++)
        if(s[i-1]!=s[i])
            cnt++;
    return cnt;
}
int main(){
    ios::sync_with_stdio(0);
    cin>>q>>s;
    reg int cnt=count(s);
    cout<<cnt/2+cnt%2<<endl;
    while(q--){
        cin>>p>>c;
        cnt=cnt-((int)(s[p-2]!=s[p-1])+(int)(s[p-1]!=s[p]))+((int)(s[p-2]!=c)+(int)(c!=s[p]));
        s[p-1]=c;
        cout<<cnt/2+cnt%2<<endl;
    }
    return 0;
}
posted @ 2023-01-09 17:32  WorldHim  阅读(66)  评论(0)    收藏  举报