题目大意
给你一个长度为nnn的序列,每个位置是A,B,CA,B,CA,B,C三个字母之一。
AAA可以打败BBB,BBB可以打败CCC,CCC可以打败AAA。
有mmm次操作,操作有两种类型:
l r x y:将区间[l,r][l,r][l,r]内的所有xxx改成yyy,yyy改成xxxl r x:你手中拿着字母xxx,要从lll走到rrr,每次你需要对比你手上的字母和当前序列位置的字母,如果当前序列位置的字母可以吃掉你手上的字母,则将你手上的字母改为当前序列位置的字母。这类操作对序列没有任何修改。求走完了[l,r][l,r][l,r]之后,你手中的物品是什么
1≤n,m≤2×1051\leq n,m\leq 2\times 10^51≤n,m≤2×105
时间复杂度为5000ms5000ms5000ms,空间复杂度为512MB512MB512MB。
题解
考虑分块。设块长为BBB。
维护每一块中,在每种对应关系下进入的字母为A/B/CA/B/CA/B/C时走完这个块出来之后是什么字母。设当前块为vvv,对应关系为v1v1v1,进入的字母为v2v2v2,则记出来的字母为wv,v1,v2w_{v,v1,v2}wv,v1,v2,因为对应关系有3!=63!=63!=6种,字母有333种,所以我们预处理出所有wv,v1,v2w_{v,v1,v2}wv,v1,v2的时间复杂度为O(18n)O(18n)O(18n)。
对于第一类操作,即区间修改,对散块暴力修改并更新www值,时间复杂度为O(36B)O(36B)O(36B);对整块只需要改变块中的对应关系即可,时间复杂度为O(3nB)O(\dfrac{3n}{B})O(B3n)。
对于第二类操作,即区间查询,对散块暴力查询,时间复杂度为O(2B)O(2B)O(2B);对整块直接用wv,v1,v2w_{v,v1,v2}wv,v1,v2查询即可,时间复杂度为O(nB)O(\dfrac nB)O(Bn)。
所以,总时间复杂度为O(q(36B+3nB))O(q(36B+\dfrac{3n}{B}))O(q(36B+B3n)),由基本不等式可得当36B=3nB36B=\dfrac{3n}{B}36B=B3n时时间复杂度最小,即B=n12B=\sqrt{\dfrac{n}{12}}B=12n,那么时间复杂度为O(312qn)O(3\sqrt{12}q\sqrt n)O(312qn)。因为跑不满,而且时限有5s5s5s,所以是可以过的。
可以参考代码帮助理解。
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;
}

文章讲述了如何解决一个关于字母关系的问题,通过分块和预处理,针对区间修改和查询操作优化时间复杂度,达到在给定限制下的最优解。
392

被折叠的 条评论
为什么被折叠?



