CF1213F Unstable String Sort 题解
在毕克老师给的贪心题单中发现这道水紫,然后在AC后发现题解做法缤纷多彩,遂决定分享自己的做法。
思路
我们记 \(T\) 为 \(S\) 排序后满足 \(\forall i\in[1,n-1],T[i]\le T[i+1]\) 的字符串,那么我们可以认为给出的排序 \(a\) , \(b\) 等价于给出了 \(T\) 中字符相同的位置。
即我们对于所有的 \(a_i=b_j=k\) 我们可以知道,\(T_{\min\{i,j\}\cdots\max\{i,j\}}\) 中的每个字符都是同一个 。
事实上我们可以得到 \(n\) 个类似于 \([\min\{i,j\},\max\{i,j\}]\) 的区间,表示区间内的字符都是同一个。
现在我们希望总的字符集合大小尽量大,因此我们只让有交的区间用同一个字符,即合并有交的区间。这一步可以按照左端点排序后贪心地维护。
维护后我们得到了一些两两不交的区间,同时由于 \(i\) 和 \(j\) 都遍历 \(1\cdots n\) ,因此这些区间的并是 \([1,n]\) ,所以直接根据这些区间来构造方案是可行的,同时我们也可以根据这些区间的数量是否小于 \(k\) 来判断是否不能做到至少用 \(k\) 个字符。
只要按照区间从左到右的顺序依次赋值为 \(a,b,c\cdots\) ,如过超出 \(26\) 个全都赋值为 \(z\) ,这样我们就能构造出 \(T\) ,此时再根据给出的排列 \(a\) 或者 \(b\) 就可以得到 \(S\) 。
这样做显然是 \(\mathcal O(n\log n)\) 或者 \(\mathcal O(n)\)的,这取决于按照左端点排序时是用计数排序还是比较排序。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#define MAXN 200000
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,j,k) for (int i=(j);i<=(k);++i)
using namespace std;
struct seg{int l,r;}num[MAXN+5],ans[MAXN+5];
//seg表示区间
//ans是最终合并后的区间,num是一开始得到的区间
inline bool cmp(seg x,seg y) {return x.l<y.l;}
int a[MAXN+5],b[MAXN+5],ina[MAXN+5],inb[MAXN+5];
//a,b如题意,ina和inb分别表示值为i在a与b中的下标
int val[MAXN+5],cnt=-1;
//val是T中每个位置赋的值,cnt是合并后区间的个数
char ch[MAXN+5];
//ch是S
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n,k,l=0,r=0;
cin>>n>>k;
rep(i,1,n) cin>>a[i];
rep(i,1,n) cin>>b[i];
rep(i,1,n) ina[a[i]]=i;
rep(i,1,n) inb[b[i]]=i;
rep(i,1,n)
num[i]=(seg){min(ina[i],inb[i]),max(ina[i],inb[i])};
sort(num+1,num+n+1,cmp);
rep(i,1,n) {
if (num[i].l<=r) r=max(r,num[i].r);
else {
ans[++cnt]=(seg){l,r};
l=num[i].l;r=num[i].r;
}
}
ans[++cnt]=(seg){l,r};
if (cnt<k) {
cout<<"NO\n";
return 0;
}
cout<<"YES\n";
rep(i,1,cnt)
rep(j,ans[i].l,ans[i].r)
val[j]=i;
rep(i,1,n)
ch[a[i]]='a'-1+min(26,val[i]);
rep(i,1,n) cout<<ch[i];
cout<<"\n";
return 0;
}

浙公网安备 33010602011771号