CF1530E Minimax
\(CF1530E\ Minimax\)
题意
给一个字符串 \(s\),要求将 \(s\) 重新排列得到 \(t\),使得:
-
对于 \(t\) 的每个前缀 \(i\),定义 \(f(i)\) 为前缀 \(i\) 的 \(border\),使得所有位置的 \(f(i)\) 最大值最小
-
满足条件的 \(t\) 的字典序最小
思路分析
思路其实很简单清晰,即:
和
然后瞎推就完了
首先不难发现一个结论:
对于任意串 \(s\),当 \(s\) 中存在至少2种字符时,必然存在 \(t\) 使得 \(f(t)=0\) 或 \(1\).
证明将在下文中体现。
然后约定几个变量:
- \(st\) 为 \(s\) 中出现的字典序最小的字符。
- \(se\) 为 \(s\) 中出现的字典序第二小的字符(如果存在至少两种字符)。
- \(th\) 为 \(s\) 中出现的字典序第三小的字符(如果存在至少三种字符)。
- \(cnt[c]\) 为 \(s\) 中字符 \(c\) 出现的次数。
- \(kinds\) 为 \(s\) 中字符种数。
for(int i='a';i<='z';i++) cnt[i]=0;
st='z'+1;int kinds=0;
scanf(" %s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++){
if(!cnt[s[i]]) kinds++;
cnt[s[i]]++;
st=min(st,s[i]);
}
那么,开始酣畅淋漓的分讨特判吧~
壹.特判 \(f(t)=0\).
不难发现,当且仅当 \(\exists \ i,cnt[i]=1\) 时,能够满足 \(f(i)=0\).比如串\(s=\)"\(bababc\)",\(t\) 显然为 "caabbb".
code
bool b=0;
for(int i='a';i<='z';i++){
if(cnt[i]==1){
pc(i);--cnt[i];
for(int j='a';j<='z';j++)
while(cnt[j]-->0) pc(j);
b=1;pt;break;
}
}
if(b) continue;
贰.特判 \(kinds=1\)
直接输出即可.
if(kinds==1){
for(int i=1;i<=n;i++) pc(st);
pt;continue;
}
叁.对于 \(f(t)=1\) 进行分讨
1. \(kinds=2\)
为了让字典序最小,显然要先尽可能多地输出 \(st\) (最多显然是\(2\)个),那么为了让 \(f(t)=1\),接着分讨。
-
\(cnt[st]=2\) 时,例如"\(abbabbb\)",直接按字典序输出即可,即"\(aabbbbb\)"
-
\(cnt[st]=3\) 时,例如"\(abbaabb\)",考虑先输出 \(2\) 个 \(st\),再用 \(se\) 分割一下,输出剩下的一个 \(st\),再输出剩下的 \(se\),即"\(aababbb\)"
-
\(cnt[st]>3\) 时,我们分讨要先输出几个 \(st\).
-
\(cnt[st]-1>cnt[se]+1\) 时,例如"\(aabbaaaa\)"不难发现此时 \(st\) 一定会连着出现至少两次,(考虑用 \(se\) 分割),如果用两个 \(st\) 开头,必然会令 \(f(t)=2\),(比如"\(aababaaa\)")。所以必然要以一个 \(st\) 开头。然后输出所有 \(se\),(至于为什么输出所有的,显然 \(f(abaaaabb)=2\)),最后输出剩下的 \(st\),即"\(abbaaaaa\)"
-
\(cnt[st]-1 \leq cnt[se]+1\) 时,例如"\(aabaabbbb\)"不难发现此时 \(st\) 可以被 \(se\) 分隔开使得没有 \(st\) 相邻,那么为了让字典序最小,我们用两个 \(st\) 开头,接着循环输出"\(se\ st\ se\ st \dots\)"即可。另外,由于 \(cnt[st]-2 < cnt[se]\) 所以剩下的一定是 \(se\)。输出即可。即"\(aabababbb\)".
-
code
for(se=st+1;se<='z';se++) if(cnt[se]) break;
if(kinds==2){
if(cnt[st]==2){
pc(st),pc(st),cnt[st]-=2;
while(cnt[se]-->0) pc(se);
}
else if(cnt[st]==3){
pc(st),pc(st),cnt[st]-=2;
pc(se),--cnt[se];
pc(st),--cnt[st];
while(cnt[se]-->0) pc(se);
}
else{
if(cnt[st]-1>cnt[se]+1){
pc(st);cnt[st]--;
while(cnt[se]-->0) pc(se);
while(cnt[st]-->0) pc(st);
}
else{
pc(st),pc(st),cnt[st]-=2;
while(cnt[st] && cnt[se]){
pc(se),pc(st);
--cnt[st];--cnt[se];
}
while(cnt[se]-->0) pc(se);
}
}
pt;continue;
}
2. \(kinds \geq 3\)
-
\(cnt[st]==2\) 以及 \(cnt[st]==3\) 时,方法同上。
-
\(cnt[st]>3\) 时,与上文类似的,令 \(els=n-cnt[st]\).讨论:
-
\(cnt[st]-1>els+1\) 时,例如"\(aabbaaacaac\)",上文已证,应使用一个 \(st\) 开头,然后用一个 \(se\) 分隔,接着输出所有 \(st\) ,为了不重复出现"\(st\ se\)",我们再用 \(th\) 分隔,然后以字典序输出剩下所有字符,即"\(abaaaaaacbc\)".
-
\(cnt[st]-1\leq els+1\) 时,例如"\(aababcabc\)",上文已证,应使用两个 \(st\) 开头,然后用剩下的其他字符将剩下的 \(st\) 分隔输出,输出完 \(st\) 后按字典序输出剩下的其他字符即可。即"\(aabababcc\)".
-
code
if(kinds>=3){
if(cnt[st]==2){
pc(st),pc(st),cnt[st]=0;
for(int i=st+1;i<='z';i++)
if(cnt[i])
while(cnt[i]-->0) pc(i);
}
else if(cnt[st]==3){
pc(st),pc(st);
pc(se),--cnt[se];
pc(st);cnt[st]=0;
for(int i=st+1;i<='z';i++)
if(cnt[i])
while(cnt[i]-->0) pc(i);
}
else{
int els=n-cnt[st];
if(cnt[st]-1>els+1){
pc(st);--cnt[st];
pc(se);--cnt[se];
while(cnt[st]-->0) pc(st);
pc(th);--cnt[th];
for(int i=st+1;i<='z';i++)
if(cnt[i])
while(cnt[i]-->0) pc(i);
}
else{
pc(st),pc(st),cnt[st]-=2;
for(int i=st+1;i<='z';i++){
while(cnt[i] && cnt[st])
pc(i),pc(st),--cnt[i],--cnt[st];
if(!cnt[st])
while(cnt[i]-->0) pc(i);
}
}
}
pt;
}
真是一场酣畅淋漓的分讨啊
\(AC\ \ code\)
#include<bits/stdc++.h>
using namespace std;
#define pc putchar
#define read read()
#define pt puts("")
inline int read
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
return f*x;
}
void write(int x)
{
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
return;
}
#define N 100010
int q,n;
char s[N];
int cnt[130];
char st='z'+1,se,th;
signed main()
{
#ifndef ONLINE_JUDGE
freopen("lty.in","r",stdin);
freopen("lty.out","w",stdout);
#endif
q=read;
while(q-->0){
for(int i='a';i<='z';i++) cnt[i]=0;
st='z'+1;int kinds=0;
scanf(" %s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++){
if(!cnt[s[i]]) kinds++;
cnt[s[i]]++;
st=min(st,s[i]);
}
bool b=0;
for(int i='a';i<='z';i++){
if(cnt[i]==1){
pc(i);--cnt[i];
for(int j='a';j<='z';j++)
while(cnt[j]-->0) pc(j);
b=1;pt;break;
}
}
if(b) continue;
if(kinds==1){
for(int i=1;i<=n;i++) pc(st);
pt;continue;
}
for(se=st+1;se<='z';se++) if(cnt[se]) break;
if(kinds==2){
if(cnt[st]==2){
pc(st),pc(st),cnt[st]-=2;
while(cnt[se]-->0) pc(se);
}
else if(cnt[st]==3){
pc(st),pc(st),cnt[st]-=2;
pc(se),--cnt[se];
pc(st),--cnt[st];
while(cnt[se]-->0) pc(se);
}
else{
if(cnt[st]-1>cnt[se]+1){
pc(st);cnt[st]--;
while(cnt[se]-->0) pc(se);
while(cnt[st]-->0) pc(st);
}
else{
pc(st),pc(st),cnt[st]-=2;
while(cnt[st] && cnt[se]){
pc(se),pc(st);
--cnt[st];--cnt[se];
}
while(cnt[se]-->0) pc(se);
}
}
pt;continue;
}
for(th=se+1;th<='z';th++) if(cnt[th]) break;
if(kinds>=3){
if(cnt[st]==2){
pc(st),pc(st),cnt[st]=0;
for(int i=st+1;i<='z';i++)
if(cnt[i])
while(cnt[i]-->0) pc(i);
}
else if(cnt[st]==3){
pc(st),pc(st);
pc(se),--cnt[se];
pc(st);cnt[st]=0;
for(int i=st+1;i<='z';i++)
if(cnt[i])
while(cnt[i]-->0) pc(i);
}
else{
int els=n-cnt[st];
if(cnt[st]-1>els+1){
pc(st);--cnt[st];
pc(se);--cnt[se];
while(cnt[st]-->0) pc(st);
pc(th);--cnt[th];
for(int i=st+1;i<='z';i++)
if(cnt[i])
while(cnt[i]-->0) pc(i);
}
else{
pc(st),pc(st),cnt[st]-=2;
for(int i=st+1;i<='z';i++){
while(cnt[i] && cnt[st])
pc(i),pc(st),--cnt[i],--cnt[st];
if(!cnt[st])
while(cnt[i]-->0) pc(i);
}
}
}
pt;
}
}
return 0;
}
\(CF\)*\(2100\) 难度,比起题单上其他题,真是挺水。