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]);
}