AT CODE FESTIVAL 2017 qualB F

First Post:

Last Update:

Word Count:
840

Read Time:
3 min

Page View: loading...

题意

给定 表示 三种字符的数量,用这些字符构造一个字符串使得其所有循环串的最小串最大。

思路

考虑贪心构造,我们不妨模拟所有开头会各形成一个字符这个过程,然后维护最小起点的集合,并始终使它们尽可能的大。

首先,它们需要满足最小性,所以它们一定是所有串中最小的。然后,我们为了使它尽可能大,会试图在这之后添加一个尽可能大的串,注意这需要对最小集合中每个串执行一次。

于是我们拿出我们最大的一车字符串,拼到它们后面,然后其中不是最小的以后就可以弹出最小集合了。

然后你发现这个过程可以被简化为拿出最大与最小的字符串,将它们合并。显然二者等价,接下来我们简单证明上面那个复杂些的做法的正确性。

证明

首先,我们合并的结果一定是一个循环最小串。因为一开始的时候循环最小串在最小起点集合中,而之后的每次操作都能保证它仍在集合中。而不在集合中的串已经大于在这里面的串了,故而它们不可能是循环最小串。

然后,不存在一个更优解。我们假设存在这么一个解,那么在某个时刻还没到冲突点的时候,我们集合里的串应该与答案的前缀是一样的,然后被我们拼上了一个不够好的串。

接下来好串定义为一轮拼接中,被拼接到后面的串中最小的那个。

那么就有两种可能,一种是我们此时手里的好串因为我们之前策略不够优所以不够多,另一种是我们的最小串太多了导致好串不够了。

对于第一种情况,不妨假设我们在之前有一个决策应当使用 来使得现在合法,然而我们没有。如果 大于等于好串,那么好串的数量不变,不能解决问题。如果 小于现在的好串,注意到当时的好串必须小于等于 ,这也就意味着实际上现在的好串大于当时的好串。这意味着大的串被留到了后面,与我们的贪心过程是矛盾的

对于第二种情况,我们应当在某次决策时尽可能减少 的使用(从而减少最小集合),然而这只能通过拼接更大 来完成,于是这与我们的贪心过程是矛盾的。

综上,我们的贪心过程合法。

CODE

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
#include<set>
#include<cstdio>
#include<string>
#include<iostream>
using namespace std;
const int N=1000010,FSIZE=1<<26;
int x;
multiset<string> s;
char BuF[FSIZE],*InF=BuF;
template<typename T>void read(T &x){
for(;*InF<33;++InF);
for(x=0;*InF>32;x=x*10+(*InF++^48));
}
int main(){
fread(BuF,1,FSIZE,stdin);
for(read(x);x--;s.insert("a"));
for(read(x);x--;s.insert("b"));
for(read(x);x--;s.insert("c"));
for(;s.size()>1;){
auto a=s.begin(),b=--s.end();
s.insert(*a+*b);
s.erase(a);
s.erase(b);
}
cout<<*s.begin();
return(0);
}