题目描述

有 $N$ 个装置排成一排,组成一个系统。每个装置有两种状态 $\texttt A$ 和 $\texttt B$。具体地,假想有一个小球从最左侧进入这个系统,则两种状态的行为如下:。

  1. $\texttt A$:将球反弹回原方向

  2. $\texttt B$:让球通过,不改变方向

当然,每当一个小球经过一个装置后,无论是以 $\texttt A$ 状态还是 $\texttt B$ 状态经过,装置都将改变原来的状态 (即 $\texttt A$ 变 $\texttt B$,$\texttt B$ 变 $\texttt A$)。

现在给定初始情况下所有装置的状态,求 $K$ 个小球经过后装置的状态。

输入格式

第一行包含两个正整数 $N, K$ ($N \leq 2 \times 10^5; K \leq 10^9$),分别表示装置的个数和经过的小球数。

第二行包含一个由 $\texttt A$ 和 $\texttt B$ 构成的字符串 $S$ ($\left| S \right| = N$),其中第 $i$ 个字符表示左起第 $i$ 个装置的状态。

输出格式

输出一行,包含一个字符串 $S_K$,表示 $K$ 次小球经过后状态的状态,具体格式同输入。

题解

首先,分析当一个小球经过时,改变的状态。下面方便起见,把 $\texttt A$ (会反弹的) 称为「墙」,把 $\texttt B$ (直接通过的) 称为「地」。

根据第一个位置 $S_1$ 是否是「墙」进行讨论:

  1. $S_1$ 是「墙」。

    此时小球将直接弹回,然后 $S_1$ 变回「地」。

  2. $S_1$ 是「地」。

    通过对较小的情况的尝试,不难发现一个结论:

    若记 $\bar x$ 为与状态 $x$ 相反的状态,则若 $S_1$ 是「地」,则新状态 (用 $T$ 表示) 满足 $T_i = \overline {S_{i+1}}$ ($1 \leq i < N$),$T_N$ 为「墙」,且小球一定从右边离开系统

    证明

    对 $\left| S \right|$ 归纳证明。当 $\left| S \right| \leq 3$ 时容易验证。

    设结论对 $\left| S \right| = N - 1$ 成立,考虑 $\left| S \right| = N$ 的情形。

    根据 $S_2$ 是否为「墙」进行讨论:

    1. $S_2$ 是「墙」。

      分析如下表 (图例:>>> 表示向右运动的球,<<< 表示向左运动的球,| 表示「墙」,_ 表示「地」)。

      (a) >>> _     |     ……
      (b)     | >>> |     ……
      (c)     | <<< _     ……
      (d)     _ >>> _     ……
      

      到上表中 (d) 步时,$T_1 = \overline {S_2} =$「地」,然后只考虑右边 $N - 1$ 个位置,不难发现它们恰好构成原问题的一个大小为 $N - 1$ 的子问题。

      由归纳假设知,$T_2, T_3, \cdots, T_N$ 满足结论中所述的状态,且球从右边离开系统,加上前面 $T_1$ 也满足,这种情况结论成立。

    2. $S_2$ 是「地」。

      分析如下表:

      (a) >>> _     _     ……
      (b)     | >>> _     ……
      

      同样,到 (b) 时就已经满足命题条件了,从而可以使用归纳假设,且 $T_1 = \overline {S_2} =$「墙」,从而这种情形结论也成立。

    综上,由归纳原理知,原命题成立。

于是我们只需要快速模拟这个过程。

考虑每次小球移动,要么是将 $S_1$ 切换,要么整体平移一位。

对于整体反转 (取反) 的问题,其实可以不用真的去 "反转",因为容易注意到,最终答案需要取反当且仅当进行了了奇数次平移

而在平移的过程中,后面补充的字符一定是从「墙」开始,「墙」「地」「墙」「地」……交错出现 (ps: 因为我们已经取消了取反操作,所以这里会变成交替)

于是接下来的任务就是计算出一共平移了多少次。

可以发现,对于情况 1 ($S_1$ 是「墙」),则不会发生平移,而对于情况 2 ($S_1$ 是「地」),则会发生平移。

那么只需要统计每个位置是否是「墙」即可,但是,同样的道理,我们取消了取反操作,应该判断奇数位是否是「墙」,偶数位是否为「地」

对于 $K \geq N$ 的部分,不难发现后面的过程非常由规律:当 $N$ 为奇数时两步一平移,$N$ 为偶数时一步一平移,因此可以 $O \left( 1 \right)$ 计算。

综上,本题可以在 $O \left( N \right)$ 时间内解决。

代码

#include <bits/stdc++.h>

const int N = 200054;

int n, K;
char s[N], t[N];

inline int get(int i) {return (i < n ? (int)s[i] : (i ^ n)) & 1;}

int main() {
	int i, j, c;
	scanf("%d%d%s", &n, &K, s);
	for (i = 0; i < n && K; ++i) {
		c = 1 + ((i ^ (int)s[i]) & 1);
		if (K >= c) K -= c;
		else break;
	}
	if (i == n && K) c = 1 + (n & 1), i += K / c, K %= c;
	for (j = 0; j < n; ++j) t[j] = get(i + j);
	for (*t ^= K, j = 0; j < n; ++j) t[j] = (i & 1 ? 65 + t[j] : 66 - t[j]);
	return puts(t), 0;
}

坑1:在平移的时候注意输出的问题,不要数组越界了。可以使用类似队列的方法来实现,不过这里没必要。

坑2:$K \geq N$ 的时候整个串的周期为 $1$ ($2 \mid N$) 或 $2$ ($2 \nmid N$),不过要注意串的形态变化是 "ρ" 形的。