题意
一个排列,每次选一个子序列按顺序放在开头,要求变成升序的操作次数不超过17次,给出方案。n<=1E5。
思考
对于ai=aj-1且i<j的数字,一定要保持其相对顺序。可以根据这个关系分成若干个块,并根据每个块最小的数从小到大将块编号为1~m。
接着把所有奇数编号的块选出来放在左侧。能发现这次操作后奇数编号和偶数编号的块至少有一半会变成更大的块。
复杂度O(nlogn)。
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1E5+5; 4 int n,a[maxn],b[maxn],bucket[22][maxn]; 5 int m,where[maxn],bel[maxn]; 6 bool check() 7 { 8 for(int i=1;i<=n;++i) 9 if(a[i]!=i) 10 return false; 11 return true; 12 } 13 int main() 14 { 15 ios::sync_with_stdio(false); 16 cin>>n; 17 for(int i=1;i<=n;++i) 18 cin>>a[i]; 19 int now=0; 20 while(!check()) 21 { 22 ++now; 23 for(int i=1;i<=n;++i) 24 bucket[now][i]=a[i]; 25 m=1; 26 for(int i=1;i<=n;++i) 27 where[a[i]]=i; 28 int pos=1; 29 while(pos<=n) 30 { 31 bel[where[pos]]=m; 32 if(where[pos]>where[pos+1]&&pos<=n) 33 ++m; 34 ++pos; 35 } 36 int tot=0; 37 for(int i=1;i<=n;++i) 38 b[i]=a[i]; 39 for(int i=1;i<=n;++i) 40 if(bel[i]&1) 41 a[++tot]=b[i]; 42 for(int i=1;i<=n;++i) 43 if(!(bel[i]&1)) 44 a[++tot]=b[i]; 45 } 46 cout<<now<<endl; 47 for(int i=1;i<=now;++i,cout<<endl) 48 for(int j=1;j<=n;++j) 49 cout<<bucket[i][j]<<" "; 50 for(int i=1;i<=n;++i) 51 cout<<a[i]<<" "; 52 cout<<endl; 53 return 0; 54 }