Skip to main content
Original286 words1 min

题解:CF570C Replacement

Summary

给定长度为 n 、由点号和小写字母构成的字符串,每次修改一个字符,并询问把所有连续点号缩成单个点号所需的替换次数。该次数等于点号总数减去连续点号段数。维护这两个量,每次修改仅影响被改位置与其左右相邻字符的关系,分类讨论字母与点号互换如何增减段数与总数,即可在常数时间内更新每次询问的答案。

题意简述​

给定长度为 nn 的字符串,包含 . 和小写字母,进行 mm 次操作,每次修改一个字符,并询问将所有连续 . 转化为单个 . 所需的次数。

解题思路​

设字符串中有 kk 个连续的 . 子串,长度分别为 aia_i。每个子串需要 ai−1a_i-1 次转化,总转化次数为:

∑i=1k(ai−1)=(∑i=1kai)−k\sum_{i=1}^k (a_i-1)=\left(\sum_{i=1}^k a_i\right)-k

注意到,∑i=1kai\sum_{i=1}^k a_i 为字符串中所有 . 的总数,在每次操作中,判断是否将某个 . 替换为字母或字母替换为 .,即可更新 . 的数量。

对于每次操作分类讨论,维护 . 子串数量:

  1. 将 . 替换为字母:如果左右两边都是字母,则 . 子串数量减少 11,转化次数加 11;如果左右两边都是 .,则 . 子串数量增加 11,转化次数减少 11。
  2. 将字母替换为 .:如果左右两边都是字母,则 . 子串数量增加 11,转化次数减少 11;如果左右两边都是 .,则 . 子串数量减少 11,转化次数增加 11。

参考代码​

445 Bcpp
#include <bits/stdc++.h>
using namespace std;

using ll=long long;
const int N=300005;
int a[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
cin>>n>>m;
int ans=0;
for(int i=1;i<=n;i++)
{
char f;
cin>>f;
a[i]=f=='.';
if(a[i]&&a[i-1])ans++;
}
while(m--)
{
int x;
char f;
cin>>x>>f;
int y=f=='.';
if(a[x]!=y)
{
a[x]=y;
ans+=(a[x-1]+a[x+1])*(y*2-1);
}
cout<<ans<<'\n';
}
return 0;
}