15145641

  这个题目的做法有很多种,大部分选择了找循环节的做法,但是个人感觉这样的做法有点暴力,所以使用了置换开方的做法来解决这个问题,时间复杂度O(n)。

  首先评价一波:是个好题!

  然后开始分析:给你N张卡片和一个洗牌机,如果位置I上的牌是J,位置J上的牌是K,那么洗牌一次后位置I上的牌就是K。首先建立一个位置->卡片的一个置换T,第一次洗牌就相当于T*T,第二次洗牌就是(T*T)*(T*T)……第K次洗牌就是T^(2^K),这就是置换的幂运算了。题目给出洗牌K次以后的置换,要求出原始置换,便需要开方运算。在kuangbin推荐的论文中我学习了开方运算,发现这是一种相对平方比较复杂的运算,因为在平方过程中,置换环会分裂,在开方往上合并的时候就难了,甚至答案都不是唯一的,但这里题目简化了,降低了开方的难度。首先牌的摆放决定了置换环长度为N,其次N为奇数,所以长度为N的置换环,经过一次平方,会分裂为gcd(2,N)个置换环,gcd(2,N)=1,所以这个置换环永远不会被分裂,这样开方就简单多了。

  首先把题目给出的置换环写出来,假设是X1 -> X2 -> X3 -> X4 -> X5,但这是经过2^K运算之后的,X1还是那个X1,X1位置都是0,但是X2和X1中间却隔了2^K个数,也就是X2在目前置换环的位置为2,但在原置换环的位置却是2^K,X3在目前置换环的位置为3,但在原置换环的位置却是2^K+2^K…… Xi的原位置就是(i-1)*(2^K),所以通过这样的重新定位,便可以求出原先的置换环,那也就找到了原先的置换,输出答案就好了。

  如果不懂位置为什么是这样变的,可以随便找一个置换环,对其做幂运算,就会发现相邻两值的差距变化了,论文中也有详细的介绍

  总结:题目考察对置换环正负幂运算的理解,当然也可以理解为对置换幂运算具有循环节特性的考察。

  代码如下:

  

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#define LL long long
const int N = 1005;
bool vis[N];
vector<int > H;
int n,s,to[N],ans[N],res[N];
int qpow(int x,int y,int Mod) {
    int res = 1;
    while(y) {
        if(y&1) {
            res = (res*x)%Mod;
        }
        x = (x*x)%Mod;
        y >>= 1;
    }
    return res;
}
int main() {
    while(EOF != scanf("%d%d",&n,&s)) {
        for(int i=1; i<=n; i++) {
            int y;
            scanf("%d",&y);
            to[i] = y; ///建立洗牌后的置换
        }
        memset(vis,0,sizeof(vis));
        int x = 1;
        H.clear();
        while(!vis[x]) {
            H.push_back(x); ///用来存储洗牌后的置换环
            vis[x] = 1;
            x = to[x];
        }
//        for(int i=0;i<H.size();i++){
//            cout<<H[i]<<" ";
//        }cout<<endl;

        int ss = qpow(2,s,n);
        for(int i=0; i<n; i++) {
            int j = (i*ss)%n; ///找到i位置的原位置j
            ans[j] = H[i];  ///给原置换环赋值
        }
        for(int i=0; i<n; i++) {
            int id = ans[i];
            int num = ans[(i+1)%n];
            res[id] = num; ///将置换环恢复为置换的形式
        }
        for(int i=1; i<=n; i++) {
            printf("%d\n",res[i]);
        }
    }
    return 0;
}

 

posted on 2017-11-19 16:58  icode-xiaohu  阅读(390)  评论(0编辑  收藏  举报