ARC110E 题解

First Post:

Last Update:

本题我是想到了个做法的,成功的过了第一个样例,后来与题解比对,发现我的做法考察的条件还是太弱了(

容易发现,最后的每个字符都是由原串的一段区间操作得来的。

把 A,B,C 的权值分别看成 ,那么一次操作相当于替换为异或和。

考虑选出一段区间能干嘛:

  1. 如果这段区间的异或和为 ,显然不能操作为单个字符。
  2. 如果这段区间只有一种字符,显然啥都干不了。
  3. 除开上述两种情况,归纳可得这段区间肯定能被操作成单个字符。我们称这类区间为 “好段”

(我之前的做法默认区间长度 ,理所当然的寄了)

然后就是利用常规的思维模式,考虑如何让原串 唯一映射到能被构造出来的串

首先 的异或和要等于 的异或和。

其次,我们在找方案的时候,只考虑每个“好段”右端点最靠前的方案。

有一个转移方法是,每次选一个最短的可行的段向后转移,如果 的最后一个字符取完了但还有剩余,就把剩下的字符全部补在后面(显然这些字符异或和肯定为 )。

为了说明正确性,我们要说明对于可以被构造的串 ,上述方法一定能够构造一个方案。

我们取出串 的任意一个方案,去掉第一个“好段”的最长的异或和 的后缀,且保证去掉后第一段仍是“好段”,把这段后缀接在第二个”好段“的前方,以此类推,这样得到的方案就能被上面的转移方法所表示。

那么我们设 表示处理了前 个字符的方案数,预处理 表示 位置往后最短的异或和为 的“好段”,据此转移即可。

最后取出 的异或和为 的位置 ,将答案加上 即可。

注意整个串清一色要特判,这个时候补 操作是错的,但答案为

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
39
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5,P = 1e9 + 7;
int n;
char s[N];
int f[N];
int ton[4],nxt[N][4];
int sum[N];
int main()
{
cin >> n >> (s + 1);
int flg = 1;
for(int i = 1;i <= n;i++)
{
if(i < n) flg &= (s[i] == s[i + 1]);
sum[i] = sum[i - 1] ^ (s[i] == 'A' ? 1 : (s[i] == 'B' ? 2 : 3));
}
if(flg) return puts("1"),0;
for(int i = 1;i <= 3;i++)
ton[i] = n + 1;
for(int i = n;i >= 0;i--)
{
ton[sum[i]] = i;
for(int k = 0;k <= 3;k++)
if(k != sum[i])
nxt[i][sum[i] ^ k] = ton[k];
nxt[i][sum[i] ^ sum[i + 1]] = i + 1;
}
f[0] = 1;
for(int i = 0;i < n;i++)
for(int k = 1;k <= 3;k++)
(f[nxt[i][k]] += f[i]) %= P;
int ans = 0;
for(int i = 1;i <= n;i++)
if(sum[i] == sum[n])
(ans += f[i]) %= P;
cout << ans << endl;
return 0;
}