[BZOJ4278]Tasowanie

Posted jefflyy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ4278]Tasowanie相关的知识,希望对你有一定的参考价值。

题目本来是挺简单的,可惜我的常数太大,用了各种黑科技才A掉

把$A$和$B$拼到一起,中间和最后加一个无穷大的分隔符,求出后缀数组之后根据$rank$贪心取即可

如果不在末尾加分隔符,可以看一看这组数据

3
2 1 1
2
2 1

拼起来后是$[2,1,1,\\infty,2,1]$,此时后缀$[2,1]$小于后缀$[2,1,1,\\infty,2,1]$,先取了$[2,1]$,然而更优解是先取$[2,1,1,\\infty,2,1]$

如果在末尾加一个分隔符,拼起来后是$[2,1,1,\\infty,2,1,\\infty$,此时后缀$[2,1,1,\\infty,2,1,\\infty]$就排在$[2,1,\\infty]$前面了

这样做的原理是假如当前取到$A,B$,$A$是$B$的前缀且$A$是第二个字符串的后缀,那么如果$B$比$A$多的部分都是很小的数,先把$B$取完是更优的,也就是说我们让$A$的末尾变为无穷大,那么就可以让$B$排在$A$前面

代码常数太大,用了输入输出优化才卡过去

技术分享图片

技术分享图片

???

#include<stdio.h>
#include<string.h>
int s[400010],rk[800010],cnt[400010];
struct pr{
	int c[2],id;
	pr(int a=0,int b=0,int d=0){c[0]=a;c[1]=b;id=d;}
}p[400010],q[400010];
bool operator!=(pr a,pr b){return a.c[0]!=b.c[0]||a.c[1]!=b.c[1];}
int max(int a,int b){return a>b?a:b;}
void sort(int n,int f){
	int i,m=0;
	memset(cnt,0,sizeof(cnt));
	for(i=1;i<=n;i++){
		cnt[p[i].c[f]]++;
		m=max(m,p[i].c[f]);
	}
	for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
	for(i=n;i>0;i--)q[cnt[p[i].c[f]]--]=p[i];
	for(i=1;i<=n;i++)p[i]=q[i];
}
void suf(int n){
	int i,l,m;
	for(i=1;i<=n;i++)rk[i]=s[i];
	for(l=1;l<=n;l<<=1){
		for(i=1;i<=n;i++)p[i]=pr(rk[i],rk[i+l],i);
		sort(n,1);
		sort(n,0);
		m=0;
		for(i=1;i<=n;i++){
			if(p[i]!=p[i-1])m++;
			rk[p[i].id]=m;
		}
	}
}
int rd(){
	char c;
	int x=0;
	for(c=getchar();c>‘9‘||c<‘0‘;c=getchar());
	x=c-‘0‘;
	for(c=getchar();c<=‘9‘&&c>=‘0‘;c=getchar())x=x*10+c-‘0‘;
	return x;
}
namespace output {
    char s[2500000];
	char *t=s;
    inline void put(int x){
        if(x==0)*t++=‘0‘;
        else{
            static int a[5];
			int n=0;
            for(;x>0;x/=10)a[++n]=x%10;
            while(n>0)*t++=‘0‘+a[n--];
        }
        *t++=‘ ‘;
    }
    inline void flush(){fwrite(s,1,t-s,stdout);}
}
using output::put;
int main(){
	int n,m,i,j;
	n=rd();
	for(i=1;i<=n;i++)s[i]=rd();
	s[n+1]=1001;
	m=rd();
	for(i=n+2;i<=n+m+1;i++)s[i]=rd();
	s[n+m+2]=1001;
	suf(n+m+2);
	i=1;
	j=n+2;
	while(i<=n&&j<=n+m+1){
		if(rk[i]<rk[j]){
			put(s[i]);
			i++;
		}else{
			put(s[j]);
			j++;
		}
	}
	for(;i<=n;i++)put(s[i]);
	for(;j<=n+m+1;j++)put(s[j]);
	*(output::t)=‘\\n‘;
	output::flush();
}

以上是关于[BZOJ4278]Tasowanie的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 4278 Tasowanie —— 后缀数组

BZOJ4278[ONTAK2015]Tasowanie 后缀数组

bzoj 4278 [ONTAK2015]Tasowanie——后缀数组

luogu P4278ybt金牌导航4-5-2带插入区间K小值(树套树做法)

HDU4278

HDU4278 Faulty Odometerd