给你一个长度为 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 xl 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 1≤n,m≤2×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(312qn)。因为跑不满,而且时限有 5 s 5s 5s,所以是可以过的。
可以参考代码帮助理解。
#include
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;
}