Alice和Bob发明了一个新的旋转游戏。首先,Bob给定N个数组成的序列,并把该序列平均分配成若干个块,每块正好包含K个数(K能整除N)。第一块由第1到第K个数构成,第二块由第K+1个数到第2K个数构成,以此类推。
接着,Bob要求Alice对这个序列进行一系列操作,操作有以下两种:
1.把每块里面的数向左或右旋转X个位置;
2.把整个序列向左或向右旋转X个位置。
注意操作2会改变每一块里面的数。在执行完一系列操作后,Alice把最终的序列告诉了Bob。Bob的任务就是找到初始序列。
这题,首先我们可以发现,若视块中所有同一个位置的数为一个团,那么我们只要知道这个团某一个数的位置我们就可以还原这个团在整个队列中的位置,这样我们只要知道所有团某一个数的位置就行了。
然而这并不好做,但有这个思路就行了。
对于一个团的某个数的位置(我们就看做是维护起始位置的数吧),那么我们可以将其位置表示成(x,y),表示成在第x个块中第y个。
对于y,我们可以发现,所有操作,我们维护i,块里第一个团的位置,这个用指针移动一下就行了,那么就可以还原块里团的顺序,这样就知道y了。
对于x,我们可以发现,只有2操作让同一个团里数的顺序改变,用数组yy前缀和一下就可以了。
具体可视代码
贴代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 100001
using namespace std;
int n,k,q,top;
int a[N][2],b[N],ans[N],d[N];
void init(){
scanf("%d %d %d",&n,&k,&q);
for (int i=1;i<=q;i++){
scanf("%d %d",&a[i][0],&a[i][1]);
a[i][1]=-a[i][1];
if (a[i][0]==1)a[i][1]=(a[i][1]%k+k)%k;
else
a[i][1]=(a[i][1]%n+n)%n;
}
for (int i=1;i<=n;i++)
scanf("%d",&b[i]);
}
int did(int x){
return x>n?x-n:x;
}
void work(){
static int x,y,z,kk;
x=1;
for (int i=q;i;i--){
if (a[i][0]==2){
d[1]+=a[i][1]/k;
z=a[i][1]%k;
if (z){
y=x-1;
if (!y)y=k;
if (z>y)
d[y+1]--,d[1]++,z-=y,d[k-z+1]++;
else
d[y-z+1]++,d[y+1]--;
}
}
x=(x+((-a[i][1])%k+k)%k)%k;
if (!x)x=k;
}
kk=n/k;
for (int i=2;i<=k;i++)
d[i]+=d[i-1];
for (int i=1;i<=k;i++){
y=x,d[x]%=kk;
for (int j=1;j<=d[x];j++){
y-=k;
if (y<=0)y+=n;
}
for (int j=1;j<=kk;j++){
ans[(j-1)*k+i]=b[y];
y=did(y+k);
}
x=x%k+1;
}
}
void write(){
for (int i=1;i<=n;i++)
printf("%d ",ans[i]);
}
int main(){
init();
work();
write();
return 0;
}