2016.4. 半期 最小表示法 sequence

nkoj 2513

Description

给定一个序列{A1,A2,...,An},保证A1>A2, ..., An。 
你要把它分成三段,每段单独翻转后按照原来的顺序组成新的序列,使新的序列字典序最小。

Input

第一行一个正整数n。 (n ≤ 200000) 
接下来n行每行一个数,第i+1行的数为所给序列的Ai

Output

共n行,每行一个数。第i行为操作后新序列的第i个数

Sample Input

5
10
1
2
3
4

Sample Output

1
10
2
4
3

Hint

样例解释 
{10,1,2,3,4} -> {10,1} {2} {3,4} -> {1,10}{2}{4,3} -> {1,10,2,4,3} 
1<=Ai<=109

分析:
将输入的数倒序存储到s[i]中,在s[1]......s[n]上跑一次最小表示法得到cut1(起点为3,要保证切3段);

再在s[1].....s[cut1-1]上跑一次最小表示法得到cut2(起点为2);

那么要打印出来的三段就是s[cut1].....s[n]   s[cut2]....s[cut1-1]    s[1]......s[cut2-1];

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=200000+5;
int n,s[maxn*2],temp[maxn*2];
int get_min(int len,int st){  //长度为len ,起点为ST; 
	int i,j,k;
	i=st;j=st+1;
	while(i<=len&&j<=len){
		for(k=0;k<len;k++)
			if(temp[i+k]!=temp[j+k])break;
		if(k==len)break;
		if(temp[i+k]>temp[j+k])i+=k+1;
		else if(temp[i+k]<temp[j+k])j+=k+1;
		if(i==j)j++;
	}
	return min(i,j);
}
void _print(int L,int R){
	for(int i=L;i<=R;i++)printf("%d\n",s[i]);
}
int main(){
	int i,cut1,cut2;
	scanf("%d",&n);
	for(i=n;i>0;i--)scanf("%d",&s[i]);  //到序存 
	for(i=1;i<=n;i++)temp[i]=temp[i+n]=s[i];  
	cut1=get_min(n,3);
	for(i=1;i<cut1;i++)temp[i]=temp[i+cut1-1]=s[i];
	cut2=get_min(cut1-1,2);
	cout<<cut1<<" "<<cut2<<endl;
	_print(cut1,n);
	_print(cut2,cut1-1);
	_print(1,cut2-1);
} 

详细注释版:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int a[400005],b[400005];
int len;
int work(int pos,int len2){
	//字符串的最小表示 
	int i,j,k;
	i=pos;j=pos+1;
	while(i<=len2&&j<=len2){
		for(k=0;k<len2;k++)//注意,k不是下标,而是长度,所以 (k=0;k<len2;k++)
			if(b[i+k]!=b[j+k])break;
		if(k==len2)break;
		if(b[i+k]>b[j+k])i=i+k+1;
		else if(b[i+k]<b[j+k])j=j+k+1;
		if(i==j)j++;
	}
	return min(i,j);
}
int main(){
	int n,m,i,j,k,ans,ans2,ans3;
	scanf("%d",&len);
	for(i=len;i>=1;i--){
		scanf("%d",&a[i]);//题目要求每一段都要颠倒,倒序输入 
	}
	for(i=1;i<=len;i++){
		b[i]=a[i];
		b[i+len]=b[i];//复制一遍 
	}
	ans=work(3,len);//得到第一次切割的位置 ,要保证后面还能切,位置>=3 
	for(i=ans;i<=len;i++)printf("%d\n",a[i]);
	for(i=1;i<ans;i++){
		//第二次的数组长度为1--ans-1 
		b[i+ans-1]=b[i];
	}
	ans2=work(2,ans-1);//要保证后面还剩一段 ,位置>=2 
	for(i=ans2;i<ans;i++)printf("%d\n",a[i]); //出答案 
	for(i=1;i<ans2;i++)printf("%d\n",a[i]);
}


你可能感兴趣的:(2016.4. 半期 最小表示法 sequence)