AGC037E 题解

First Post:

Last Update:

人类智慧题,对于这种题确实不太会。接受现实,多加练习(

这题一眼看过去没有什么思路或性质。需要字典序最小,我们只能从最小的字符开始考察,设其为

我们的首要目标是在最后最大化最开头那段 的长度。

如果在翻转之前,有一段 处在 的末尾,那么翻转过后,其会变成原来的两倍长,如果处在其它地方,你只能多得到一段长度相等的

所以,对于 ,只需输出 即可。

对于剩下的情况,我们可以用开头的一次操作把最长的 放到串的末尾,然后不断倍长,在最后一次操作时把这段 放在结果串的开头。

接下来我们要让 后面的那一段的字典序最小。

设那段最长的 ,它前面的串为 。我们先用一次操作把 提到 的最后,再一次操作后变为 ,容易发现,新的串必然是 的一段后缀,如此下去,最后的串必然是 的一段前缀,我们只需找出字典序最小的

有意思的地方是,由于 为回文串,只要 中出现, 一定会在 中出现,那么我们找字典序最小的 ,只需在 中找到字典序最小的长度为 的子串即可。这个可以 完成。

上面的做法也统一了 的情况,最终的时间复杂度为

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
40
41
42
43
44
45
#include <bits/stdc++.h>
using namespace std;
const int N = 5e3 + 5;
int n,k;
char s[N << 1];
char ans[N];
inline void Useans(int p)
{
for(int i = 1;i <= n;i++)
ans[i] = s[p + i - 1];
}
int fir;
inline void Check(int p)
{
if(!fir) Useans(p),fir = true;
for(int i = 1;i <= n;i++)
{
if(ans[i] < s[i + p - 1]) return;
if(ans[i] > s[i + p - 1]) return Useans(p);
}
}
int main()
{
cin >> n >> k;
cin >> (s + 1);
if(k >= 15)
{
char c = 'z';
for(int i = 1;i <= n;i++) c = min(c,s[i]);
for(int i = 1;i <= n;i++) putchar('c');
putchar('\n');
return 0;
}
for(int i = 1;i <= n;i++) s[n * 2 + 1 - i] = s[i];
for(int i = 1;i <= n + 1;i++) Check(i);
int mx = 0;
for(int i = 1;i <= n + 1;i++)
if(ans[i] == ans[1]) ++mx;
else break;
int len = mx << (k - 1);
for(int i = 1;i <= len;i++) putchar(ans[1]);
for(int i = len + 1;i <= n;i++) putchar(ans[i - len + mx]);
putchar('\n');
return 0;
}