Day4 T1 数列变换
题目
小X看到堆成山的数列作业十分头疼,希望聪明的你来帮帮他。考虑数列 \(A = [A_1, A_2 \dots A_n]\),定义变换 \(f(A, k) = [A_2, A_3 \dots A_k, A_1, A_{k+2}, A_{k+3} \dots A_{2k}, A_{k+1} \dots]\),也就是把 \(A\) 分段,每段 \(k\) 个(最后如果不足 \(k\) 个,全部分到新的一段里,详见样例),然后将每段的第一个移动到该段的最后一个。
现在,小X想知道 \(f(f(f(f([1,2,3,⋯,n],2),3),⋯),n)\) 的结果。
输入
输入一行包含一个整数 \(n\)。
输出
输出一行包含 \(n\) 个整数,表示最终的数列。
样例
输入 | 输出 |
---|---|
4 | 4 2 3 1 |
数据范围
对于 \(60%\) 的数据,\(1 \leq n \leq 10^3\)。
对于 \(100%\) 的数据,\(1 \leq n \leq 10^6\)。
题解
仔细观察可以发现,每次前一段的第一个数移动到了后一段的第一个。
每次将前一段的第一个数移动到后一段的第一个的位置,那么每次将前一段的第一个数移动到后一段的第一个,最终整个数列总的就移动了 \(n - 1\) 次。只需开一个两倍大的数组,再记录当前数列的头部位置即可。
(我这里用了滚动数组,看了一位Dalao的代码,发现只需用swap
即可orz)
#include<cstdio>
#include<algorithm>
using namespace std;
int n,head,inv=0;
int a[2000010]={0},t[2]={0};
void init(){
scanf("%d",&n);
for(int i=1;i<=n;i++) a[i]=i;
head=1;
}
void work(int k){
for(int i=head;i<=n+head-1;i+=k){
t[inv]=a[i];
a[i]=t[inv^1];
inv^=1;
if(i+k>n+head-1) a[n+head]=t[inv^1];
}
}
int main(){
init();
for(int i=2;i<=n;i++){
work(i);
head++;
}
for(int i=head;i<n+head-1;i++) printf("%d ",a[i]);
printf("%d",a[n+head-1]);
return 0;
}