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;
}