Deltix Round, Autumn 2021 (rated, Div. 1 + Div. 2) E. William The Oblivious(线段树+子序列 线段树维护转移矩阵)

题目

长度为n(n<=1e5)的仅由abc三种字母组成的串,

q(q<=1e5)次操作,每次给出两个参数pos x(x属于a、b、c三种字母中的一种),

表示把pos位置的字母改成x,

每次改完之后,都需要输出把整个串改成不包含abc子序列的最小改动次数

思路来源

cf评论区两张图

 

2019 南昌网络赛(The 2019 Asia Nanchang)B、C(线段树维护转移矩阵)、D(母函数+分治FFT)、E、G、H(分块优化矩阵块速幂)、I题待补、D题待补、A题待补_Code92007的博客-CSDN博客

题解

与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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值