这题好有趣啊,玩了好一会儿才得出比较简洁的做法。
记A状态为1(黑),B状态为0(白)。
发现几个比较有用的规律:
(1)一个球通过一段长度为L的黑块时,会把前L-1个黑块都变成白块。
特殊情况:这段黑块在最左边,那么球只会把第一个黑块取反,然后从左端飞回去;
(2)一个球通过一段长度为L的白块时,会把前L-1个白块都变成黑块;
特殊情况:这段白块在最右边,那么球会把所有白块都取反,直接从右端飞出。
这样我们的模拟过程就没原来那么烧脑了。
然而这样还是很复杂啊,根本没法推那么多步。
继续画样例,发现更有用的规律:
最左边的块为白块时,一个球飞进来,改变效果如下:
0011100001111000111
1000111100001110001
其实就是把红色的部分往前移动了一格,并且全部取反!
并且再移动几次,我们又可以发现蓝色部分的规律:
1000111100001110001
1110000111100011101
0011110000111000101
1000011110001110101
……
忽略开头为1时的操作,我们只要执行n次,蓝色部分就会侵占整个序列,
n为偶数时,序列是0101....0101,压根就不会再发生变化,所以直接就知道k次之后的结果;
n为奇数时,序列是10101...0101,除了第一位不断闪烁变化,后面也都不会变。所以根据奇偶判断第一位,后面直接输出即可。
因此,我们至多模拟2n次。
还有一些细节,就是怎么O(1)维护一次操作。
我们直接修改太慢了,不妨维护一个头指针p,表示红色部分的开头是初始串的第几位,再维护一个取反标记rev,rev=1表示红色集体取反,rev=0表示红色部分不取反,开头是1则执行:a[p]^=1;开头是0则执行:p++,rev^=1。
复杂度O(n)。
#include <cstdio>
#include <cstring>
#define rep(i,j,k) for (i=j;i<=k;i++)
using namespace std;
const int N=2e5+5;
int n,k,i,j,p,rev,a[N];
char s[N];
void trans(int x) { if (x) putchar('A'); else putchar('B'); }
int main()
{
scanf("%d%d%s",&n,&k,s+1);
rep(i,1,n) if (s[i]=='A') a[i]=1; else a[i]=0;
for (j=0,p=1;j<k && p<=n;j++) {
if (a[p]^rev) {a[p]^=1; continue;}
rev^=1; p++;
}
if (j>=k) {
rep(i,p,n) trans(a[i]^rev);
rep(i,n+1,n+p-1) trans((n+p-i)&1);
}
else {
if (n&1) {
trans((k-j+1)&1);
rep(i,2,n) trans(i&1);
}
else rep(i,1,n) trans((i+1)&1);
}
return 0;
}