抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

题目传送门

题意

有一个大小为 \(n\) 的环,环上每个点有一个颜色(黑/白)。定义一次操作是,对于每个点 \(i\),若他和他相邻的两个点中,白点多于黑点,则在新的环中, \(i\) 变成白色,否则变为黑色。

求进行 \(k\) 次操作后每个点的颜色。

\(n\le 2\times 10^5,k\le 10^9\)

题解

变色的条件相当于 \(i\) 相邻两个点的颜色都与 \(i\) 的颜色不同。对于一段长度 \(\ge 2\) 的颜色相同的段,显然颜色是永远不会变的。只有这样的段之间的这些颜色交替改变的点的颜色会发生变化。例如 WWWBWBWBWBBB -> WWWWBWBWBBBB -> WWWWWBWBBBBB -> WWWWWWBBBBBB

考虑中间的颜色交替改变的一段(如上例中 BWBWBW),发现每进行一次操作,两边的点会向两边的颜色相同的连续段合并,中间的点会改变颜色。记这样的段中某个点 \(i\) 到两边颜色相同连续段的距离是 \(a_i,b_i\),那么他会在第 \(\min(a_i,b_i)\) 次操作被合并,颜色要看两边段的颜色和具体往哪边合并,被合并之后颜色就不会再变化,而在被合并之前颜色则是交替变化。所以只要根据 \(k\)\(\min(a_i,b_i)\) 的大小进行讨论即可。

要注意的是,可能题目给定字符串的最前面一段和最后面一段是可以拼起来的,所以要对顺序进行一定的调整。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <cstdio>
#include <algorithm>
#define N 400005
int n, k, d, dis1[N], dis2[N];
char a[N], b[N];
bool check(){
if (n & 1) return 0;
for (register int i = 2; i <= n; ++i)
if (a[i] == a[i - 1]) return 0;
return 1;
}
int main(){
scanf("%d%d%s", &n, &k, a + 1);
if (check()){
if (k & 1) std :: reverse(a + 1, a + 1 + n);
return printf("%s", a + 1), 0;
}
for (register int i = 1; i <= n; ++i) a[n + i] = a[i];
for (register int i = n + 2; i < (n << 1); ++i)
if (a[i - 1] != a[i] && a[i] != a[i + 1]) continue;
else{ d = i - 1; break; }
if (!d) d = (n << 1) - 1;
for (register int i = d - n + 1; i <= d; ++i)
if (a[i - 1] != a[i] && a[i] != a[i + 1]) dis1[i] = dis1[i - 1] + 1;
else dis1[i] = 0;
for (register int i = d; i > d - n; --i)
if (a[i - 1] != a[i] && a[i] != a[i + 1]) dis2[i] = dis2[i + 1] + 1;
else dis2[i] = 0;
for (register int i = d - n + 1; i <= d; ++i)
if (a[i - 1] != a[i] && a[i] != a[i + 1])
if (k >= std :: min(dis1[i], dis2[i]))
if (dis1[i] < dis2[i]) b[i] = a[i - dis1[i]];
else b[i] = a[i + dis2[i]];
else b[i] = a[i] == 'B' && (k & 1) || (a[i] == 'W' && (k & 1 ^ 1)) ? 'W' : 'B';
else b[i] = a[i];
for (register int i = 1; i <= d - n; ++i) putchar(b[i + n]);
for (register int i = d - n + 1; i <= n; ++i) putchar(b[i]);
}

评论