题意
给定 表示 三种字符的数量,用这些字符构造一个字符串使得其所有循环串的最小串最大。
思路
考虑贪心构造,我们不妨模拟所有开头会各形成一个字符这个过程,然后维护最小起点的集合,并始终使它们尽可能的大。
首先,它们需要满足最小性,所以它们一定是所有串中最小的。然后,我们为了使它尽可能大,会试图在这之后添加一个尽可能大的串,注意这需要对最小集合中每个串执行一次。
于是我们拿出我们最大的一车字符串,拼到它们后面,然后其中不是最小的以后就可以弹出最小集合了。
然后你发现这个过程可以被简化为拿出最大与最小的字符串,将它们合并。显然二者等价,接下来我们简单证明上面那个复杂些的做法的正确性。
证明
首先,我们合并的结果一定是一个循环最小串。因为一开始的时候循环最小串在最小起点集合中,而之后的每次操作都能保证它仍在集合中。而不在集合中的串已经大于在这里面的串了,故而它们不可能是循环最小串。
然后,不存在一个更优解。我们假设存在这么一个解,那么在某个时刻还没到冲突点的时候,我们集合里的串应该与答案的前缀是一样的,然后被我们拼上了一个不够好的串。
接下来好串定义为一轮拼接中,被拼接到后面的串中最小的那个。
那么就有两种可能,一种是我们此时手里的好串因为我们之前策略不够优所以不够多,另一种是我们的最小串太多了导致好串不够了。
对于第一种情况,不妨假设我们在之前有一个决策应当使用 来使得现在合法,然而我们没有。如果 大于等于好串,那么好串的数量不变,不能解决问题。如果 小于现在的好串,注意到当时的好串必须小于等于 ,这也就意味着实际上现在的好串大于当时的好串。这意味着大的串被留到了后面,与我们的贪心过程是矛盾的
对于第二种情况,我们应当在某次决策时尽可能减少 的使用(从而减少最小集合),然而这只能通过拼接更大 来完成,于是这与我们的贪心过程是矛盾的。
综上,我们的贪心过程合法。
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); }
|