NOIP2023模拟16联测37 小猫吃火龙果

52 篇文章 0 订阅

题目大意

给你一个长度为 n n n的序列,每个位置是 A , B , C A,B,C A,B,C三个字母之一。

A A A可以打败 B B B B B B可以打败 C C C C C C可以打败 A A A

m m m次操作,操作有两种类型:

  • l r x y:将区间 [ l , r ] [l,r] [l,r]内的所有 x x x改成 y y y y y y改成 x x x
  • l r x:你手中拿着字母 x x x,要从 l l l走到 r r r,每次你需要对比你手上的字母和当前序列位置的字母,如果当前序列位置的字母可以吃掉你手上的字母,则将你手上的字母改为当前序列位置的字母。这类操作对序列没有任何修改。求走完了 [ l , r ] [l,r] [l,r]之后,你手中的物品是什么

1 ≤ n , m ≤ 2 × 1 0 5 1\leq n,m\leq 2\times 10^5 1n,m2×105

时间复杂度为 5000 m s 5000ms 5000ms,空间复杂度为 512 M B 512MB 512MB


题解

考虑分块。设块长为 B B B

维护每一块中,在每种对应关系下进入的字母为 A / B / C A/B/C A/B/C时走完这个块出来之后是什么字母。设当前块为 v v v,对应关系为 v 1 v1 v1,进入的字母为 v 2 v2 v2,则记出来的字母为 w v , v 1 , v 2 w_{v,v1,v2} wv,v1,v2,因为对应关系有 3 ! = 6 3!=6 3!=6种,字母有 3 3 3种,所以我们预处理出所有 w v , v 1 , v 2 w_{v,v1,v2} wv,v1,v2的时间复杂度为 O ( 18 n ) O(18n) O(18n)

对于第一类操作,即区间修改,对散块暴力修改并更新 w w w值,时间复杂度为 O ( 36 B ) O(36B) O(36B);对整块只需要改变块中的对应关系即可,时间复杂度为 O ( 3 n B ) O(\dfrac{3n}{B}) O(B3n)

对于第二类操作,即区间查询,对散块暴力查询,时间复杂度为 O ( 2 B ) O(2B) O(2B);对整块直接用 w v , v 1 , v 2 w_{v,v1,v2} wv,v1,v2查询即可,时间复杂度为 O ( n B ) O(\dfrac nB) O(Bn)

所以,总时间复杂度为 O ( q ( 36 B + 3 n B ) ) O(q(36B+\dfrac{3n}{B})) O(q(36B+B3n)),由基本不等式可得当 36 B = 3 n B 36B=\dfrac{3n}{B} 36B=B3n时时间复杂度最小,即 B = n 12 B=\sqrt{\dfrac{n}{12}} B=12n ,那么时间复杂度为 O ( 3 12 q n ) O(3\sqrt{12}q\sqrt n) O(312 qn )。因为跑不满,而且时限有 5 s 5s 5s,所以是可以过的。

可以参考代码帮助理解。

code

#include<bits/stdc++.h>
using namespace std;
const int N=200000,V=2000;
int n,m,bl,a[N+5],be[V+5][3],re[3][3][3],pd[3][3];
int to[6][3]={{0,1,2},{0,2,1},{1,0,2},{1,2,0},{2,0,1},{2,1,0}};
int w[V+5][6][3];
char s[N+5];
char rdc(){
	char ch=getchar();
	while(ch<'A'||ch>'C') ch=getchar();
	return ch;
}
int pos(int i){
	return (i-1)/bl+1;
}
int gtpd(int x,int y){
	if(x==0&&y==1||x==1&&y==2||x==2&&y==0) return 1;
	if(x==y) return 0;
	return -1;
}
int wh(int v){
	return re[be[v][0]][be[v][1]][be[v][2]];
}
void gtw(int v){
	for(int v1=0;v1<6;v1++){
		for(int v2=0;v2<3;v2++){
			int vt=v2;
			for(int i=v*bl-bl+1;i<=min(v*bl,n);i++){
				if(pd[to[v1][a[i]]][vt]==1) vt=to[v1][a[i]];
			}
			w[v][v1][v2]=vt;
		}
	}
}
void init(){
	for(int i=0;i<3;i++){
		for(int j=0;j<3;j++) pd[i][j]=gtpd(i,j);
	}
	for(int i=0;i<6;i++){
		re[to[i][0]][to[i][1]][to[i][2]]=i;
	}
	for(int i=1;i<=pos(n);i++){
		gtw(i);
		be[i][0]=0;be[i][1]=1;be[i][2]=2;
	}
}
void work(int l,int r,int x,int y){
	int vl=pos(l);
	for(int i=vl*bl-bl+1;i<=min(vl*bl,n);i++){
		a[i]=be[vl][a[i]];
	}
	be[vl][0]=0;be[vl][1]=1;be[vl][2]=2;
	for(int i=l;i<=r;i++){
		if(a[i]==x) a[i]=y;
		else if(a[i]==y) a[i]=x;
	}
	gtw(vl);
}
void ch(int l,int r,int x,int y){
	int vl=pos(l),vr=pos(r);
	if(vl==vr){
		work(l,r,x,y);return;
	}
	work(l,vl*bl,x,y);
	work(vr*bl-bl+1,r,x,y);
	for(int i=vl+1;i<=vr-1;i++){
		for(int j=0;j<3;j++){
			if(be[i][j]==x) be[i][j]=y;
			else if(be[i][j]==y) be[i][j]=x;
		}
	}
}
int find(int l,int r,int x){
	int vl=pos(l),vr=pos(r);
	if(vl==vr){
		for(int i=l;i<=r;i++){
			if(pd[be[vl][a[i]]][x]==1) x=be[vl][a[i]];
		}
		return x;
	}
	for(int i=l;i<=vl*bl;i++){
		if(pd[be[vl][a[i]]][x]==1) x=be[vl][a[i]];
	}
	for(int i=vl+1;i<=vr-1;i++){
		x=w[i][wh(i)][x];
	}
	for(int i=vr*bl-bl+1;i<=r;i++){
		if(pd[be[vr][a[i]]][x]==1) x=be[vr][a[i]];
	}
	return x;
}
int main()
{
//	freopen("training.in","r",stdin);
//	freopen("training.out","w",stdout);
	scanf("%d%d",&n,&m);
	if(n<=100) bl=sqrt(n);
	else bl=sqrt(n/12);
	scanf("%s",s+1);
	for(int i=1;i<=n;i++) a[i]=s[i]-'A';
	init();
	while(m--){
		int tp,l,r;
		char x,y;
		scanf("%d",&tp);
		if(tp==0){
			scanf("%d%d",&l,&r);
			x=rdc();y=rdc();
			ch(l,r,x-'A',y-'A');
		}
		else{
			scanf("%d%d",&l,&r);
			x=rdc();
			printf("%c\n",find(l,r,x-'A')+'A');
		}
	}
	return 0;
}
  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值