GDOI2016模拟8.8旋转

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值