题目
长度为n(n<=1e5)的仅由abc三种字母组成的串,
q(q<=1e5)次操作,每次给出两个参数pos x(x属于a、b、c三种字母中的一种),
表示把pos位置的字母改成x,
每次改完之后,都需要输出把整个串改成不包含abc子序列的最小改动次数
思路来源
cf评论区两张图
题解
与hello2016(cf750E)那道题类似,2019南昌网络赛出过一次,
不含abc序列的最小代价,
1. 前半段没有a(说明第一个a出现在后半段),后半段没有abc
2. 前半段没有ab(说明前半段还是可能有a,还说明能构成ab的第一个b出现在后半段),后半段没有bc
3. 前半段没有abc(说明第一段里还是可能有ab),后半段没有c
线段树,每个位置维护一个3*3矩阵,代表3种状态
c[0][0]状态:没有a c[1][1]状态:没有b c[2][2]状态:没有c
这样c[0][1]就是没有ab,c[1][2]没有bc,c[0][2]没有abc,实际有7个合法状态
转移的时候只考虑这六种合法的转移,按照1、2、3三种可能性拼接即可
发现不含序列和包含2016序列类似,都是线段树维护矩阵,
区别在于,
不含abc序列时,每个点的状态,是不含一个单独的字母,如a/b/c,
而包含2016序列时,每个点的状态,是包含一个序列前缀,如空串/2/20/201/2016,
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int INF=0x3f3f3f3f;
int n,q,x,v;
char s[N],t[5];
struct mat{
const static int MAXN = 3;
int c[MAXN][MAXN];
int m, n;
mat(){
m=n=MAXN;
memset(c,0,sizeof (c));
}
mat(int a, int b) : m(a), n(b) {
memset(c, 0, sizeof (c));
}
void reset(int a,int b){
m=a; n=b;
}
void clear(){
memset(c, 0, sizeof(c));
}
mat operator + (const mat& temp) {
mat ans(m, temp.n);
for (int i = 0; i < m; i++)
for (int j = i; j < temp.n; j++) {
ans.c[i][j] = INF;
for (int k = i; k <= j; k++){
ans.c[i][j] = min(ans.c[i][j] , c[i][k] + temp.c[k][j]);
}
}
return ans;
}
}dat[N*4];
void pushup(int p) {
dat[p]=dat[p<<1]+dat[p<<1|1];
}
void init(int p,int l,int r) {
dat[p].clear();
int v=s[l]-'a';
dat[p].c[v][v]=1;
}
void build(int p,int l,int r) {
dat[p].clear();
if(l==r) {
init(p,l,r);
return;
}
int mid=(l+r)/2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(p);
}
void chg(int p,int l,int r,int x,int v) {
if(l==r){
dat[p].clear();
dat[p].c[v][v]=1;
return;
}
int mid=(l+r)/2;
if(x<=mid)chg(p<<1,l,mid,x,v);
else chg(p<<1|1,mid+1,r,x,v);
pushup(p);
}
mat ask(int p,int l,int r,int ql,int qr) {
if(ql<=l && r<=qr)return dat[p];
int mid=(l+r)/2;
if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr);
if(qr<=mid)return ask(p<<1,l,mid,ql,qr);
return ask(p<<1,l,mid,ql,mid)+ask(p<<1|1,mid+1,r,mid+1,qr);
}
int main() {
scanf("%d%d",&n,&q);
scanf("%s",s+1);
build(1,1,n);
while(q--) {
scanf("%d%s",&x,t);
chg(1,1,n,x,t[0]-'a');
mat res=ask(1,1,n,1,n);
printf("%d\n",min(res.c[0][0],min(res.c[0][1],res.c[0][2])));
}
return 0;
}