• [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('
    ');
    	}
    } 
    
  • 相关阅读:
    const,var,let区别(转载)
    在windows上搭建redis集群
    Linux学习笔记—vim程序编辑器
    Linux学习笔记—文件与文件系统的压缩与打包(转载)
    Linux学习笔记—Linux磁盘与文件系统管理(转载)
    五,mysql优化——sql语句优化小技巧
    八,mysql优化——读写分离
    六,mysql优化——小知识点
    七,mysql优化——表的垂直划分和水平划分
    三,mysql优化--sql语句优化之索引一
  • 原文地址:https://www.cnblogs.com/birchtree/p/12219529.html
Copyright © 2020-2023  润新知