[Luogu2870] [USACO07DEC]最佳牛线Best Cow Line(贪心+后缀数组)
Posted birchtree
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Luogu2870] [USACO07DEC]最佳牛线Best Cow Line(贪心+后缀数组)相关的知识,希望对你有一定的参考价值。
[Luogu2870] [USACO07DEC]最佳牛线Best Cow Line(贪心+后缀数组)
题面
FJ打算带他的(N(1 leq N leq 30,000))头奶牛去参加一年一度的“全美农场主大奖赛”。在这场比赛中,每个参赛者都必须让他的奶牛排成一列,然后领她们从裁判席前依次走过。 今年,竞赛委员会在接受队伍报名时,采用了一种新的登记规则:他们把所有队伍中奶牛名字的首字母取出,按它们对应奶牛在队伍中的次序排成一列(比如说,如果FJ带去的奶牛依次为Bessie、Sylvia、Dora,登记人员就把这支队伍登记为BSD)。登记结束后,组委会将所有队伍的登记名称按字典序升序排列,就得到了他们的出场顺序。 FJ最近有一大堆事情,因此他不打算在这个比赛上浪费过多的时间,也就是说,他想尽可能早地出场。于是,他打算把奶牛们预先设计好的队型重新调整一下。 FJ的调整方法是这样的:每次,他在原来队列的首端或是尾端牵出一头奶牛,把她安排到新队列的尾部,然后对剩余的奶牛队列重复以上的操作,直到所有奶牛都被插到了新的队列里。这样得到的队列,就是FJ拉去登记的最终的奶牛队列。 接下来的事情就交给你了:对于给定的奶牛们的初始位置,计算出按照FJ的调整规则所可能得到的字典序最小的队列。
分析
我们贪心从两边往中间删除。每一次我们需要决定是从队头删除还是队尾删除。显然应该删除字符更小的那个。但是两个字符相等的时候怎么办呢?
比如我们删除到( ext{CDADBC}),显然删除右边的( ext{C})更优,因为这样可以更早的取到字典序更小的字符串( ext{BD}). 由于从队尾删除比较字典序要倒序比较,我们不如把字符串镜像反转再接在一起,变成( ext{CDA[x]DBCCBD[x']ADC})(注意括号内的并不是字符,只是一个标记).那么对于两个C的中点x,我们找到中点x反转后的对应位置(x'),容易发现,我们要比较的就是(x)和(x')左侧的两个子串( ext{DA})和( ext{BD}),直接转换成比较对应的后缀rank大小即可。注意我们不需要对每次删除后的串跑一遍,只需要一开始的时候用后缀数组预处理整个串即可。
代码
//https://www.luogu.com.cn/problem/P2870
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 500000
#define maxs 128
using namespace std;
int n;
char s[maxn*2+5];
char t[maxn*2+5];
void rsort(int *ans,int *fi,int *se,int sz,int maxv){
static int buck[maxn*2+5];
for(int i=0;i<=maxv;i++) buck[i]=0;
for(int i=1;i<=sz;i++) buck[fi[i]]++;
for(int i=1;i<=maxv;i++) buck[i]+=buck[i-1];
for(int i=sz;i>=1;i--) ans[buck[fi[se[i]]]--]=se[i];
}
int sa[maxn*2+5],rk[maxn*2+5],height[maxn*2+5];
void suffix_sort(char *str,int n){
static int se[maxn*2+5];
for(int i=1;i<=n;i++){
rk[i]=str[i];
se[i]=i;
}
rsort(sa,rk,se,n,maxs);
for(int k=1,m=maxs;k<=n;k*=2){
int p=0;
for(int i=n-k+1;i<=n;i++) se[++p]=i;
for(int i=1;i<=n;i++){
if(sa[i]>k) se[++p]=sa[i]-k;
}
rsort(sa,rk,se,n,m);
swap(rk,se);
p=rk[sa[1]]=1;
for(int i=2;i<=n;i++){
if(se[sa[i-1]]==se[sa[i]]&&se[sa[i-1]+k]==se[sa[i]+k]) rk[sa[i]]=p;
else rk[sa[i]]=++p;
}
if(p==n) break;
m=p;
}
}
bool cmp(int l,int r){
if(s[l]<s[r]) return 0;
else if(s[l]>s[r]) return 1;
else{
if(rk[l]<=rk[n*2-r+1]) return 0;
else return 1;
}
}
char ans[maxn+5];
int main(){
char ch[10];
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",ch);
s[i]=ch[0];
}
for(int i=1;i<=n;i++) t[i]=s[i];
for(int i=1;i<=n;i++) t[n+i]=s[n-i+1];
suffix_sort(t,n*2);
int l=1,r=n;
int sz=0;
while(l<=r){
if(cmp(l,r)) ans[++sz]=s[r--];
else ans[++sz]=s[l++];
}
for(int i=1;i<=sz;i++){
putchar(ans[i]);
if(i%80==0) putchar('
');
}
}
以上是关于[Luogu2870] [USACO07DEC]最佳牛线Best Cow Line(贪心+后缀数组)的主要内容,如果未能解决你的问题,请参考以下文章
贪心 洛谷P2870 [USACO07DEC]最佳牛线,黄金Best Cow Line, Gold
P2870 [USACO07DEC]Best Cow Line G
P2870 [USACO07DEC]最佳牛线,黄金Best Cow Line, Gold
P2870 [USACO07DEC]最佳牛线,黄金Best Cow Line, Gold 解题报告