POJ 1271 CARDS 置换/循环节/置换群开方

原创 2012年03月21日 23:01:24

题意:直接引用潘震皓的论文《置换群快速幂运算》。

[问题描述]

剀剀和凡凡有N张牌(依次标号为1,2,……,N)和一台洗牌机。假设N是奇数。洗牌机的功能是进行如下的操作:对所有位置I(1≤I≤N),如果位置I上的牌是J,而且位置J上的牌是K,那么通过洗牌机后位置I上的牌将是K。

剀剀首先写下一个1~N的排列ai,在位置ai处放上数值ai+1的牌,得到的顺序x1,x2, ..., xN作为初始顺序。他把这种顺序排列的牌放入洗牌机洗牌S次,得到牌的顺序为p1,p2, ..., pN。现在,剀剀把牌的最后顺序和洗牌次数告诉凡凡,要凡凡猜出牌的最初顺序x1, x2,..., xN

[输入]

第一行为整数N和S。1≤N≤1000,1≤S≤1000。第二行为牌的最终顺序p1,p2, ..., pN

[输出]

为一行,即牌的最初顺序x1,x2, ..., xN


注:在置换群中有一个定理:设,(T为一置换,e为单位置换(映射函数为的置换)),那么k的最小正整数解是T的拆分的所有循环长度的最小公倍数。或者有个更一般的结论:设,(T为一循环,e为单位置换),那么k的最小正整数解为T的长度。

[算法分析]

很显然,这题的一副扑克牌就是一个置换,而每一次洗牌就是这个置换的平方运算。由于牌的数量是奇数,并且一开始是一个大循环,所以做平方运算时候不会分裂。所以,在任意时间,牌的顺序所表示的置换一定是一个大循环。

那么根据文章开头提到的定理:设,(T为一循环,e为单位置换),那么k的最小正整数解为T的长度。

可以知道,这个循环的n次方是单位循环,换句话说,如果k mod n=1,那么这个循环的k次方,就是它本身。我们知道,每一次洗牌是一次简单的平方运算,洗x次就是原循环的2x次方。

因为n是奇数,2x mod n=1一定有一个<n的整数解,假设这个解是a;那也就是说,一幅牌,洗a次,就会回到原来的顺序。使用最终顺序不停地洗,直到回到原始顺序,求出循环节长度a以后,再单纯地向前模拟a-s次,就可以得到原始顺序了。

上面的算法是出题方给出的标准算法。显然,时间复杂度为O(n2+logs)。

换一个方向:给定了结果和s以后,可以简单地将这个目标置换用3.1节的方法开方s次得到结果。时间复杂度为O(n*s)。

或者可以更简单地,算出2s,将目标置换直接开2s次方。这里有一个技巧,因为在开方时只需要在循环中前进2s次,所以我们只关心(2s) mod n,也就免去了大数字的运算。所以,计算2s需要O(logs),而开方需要O(n)。整个时间复杂度为O(n+logs)。


方法一:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define MAXN 1100
int p[MAXN];
bool flag[MAXN];

void permute ( int n ) //模拟置换
{
    int now, i, tmp[MAXN];
    memset(flag,0,sizeof(flag));
    for ( i = 1; i <= n; i++ )
    {
        if ( flag[i] ) continue;
        now = i;
        while ( flag[now] == 0 )
        {
            flag[now] = 1;
            tmp[now] = p[p[now]];
            now = p[p[now]];
        }
    }
    for ( i = 1; i <= n; i++ ) p[i] = tmp[i];
}

int mod_exp ( int a, int b, int n )
{
    int ret = 1;
    a = a % n;
    while ( b >= 1 )
    {
        if ( b & 1 )
            ret = ret * a % n;
        a = a * a % n;
        b >>= 1;
    }
    return ret;
}

int main()
{
    int n, s, a, i;
    scanf("%d%d",&n,&s);
    for ( i = 1; i <= n; i++ )
        scanf("%d",&p[i]);
    for ( a = 1; mod_exp(2,a,n) != 1; a++ );
    s = s % a;
    for ( i = 1; i <= a - s; i++ )
        permute ( n );
    for ( i = 1; i <= n; i++ )
        printf("%d\n",p[i]);
    return 0;
}



OI生涯回忆录 && NOI2016游记

NOI结束了,混了个rank80+thu60分降分。。回寝室写写回忆录吧。。     最开始学OI也是机缘巧合。我数学一直不是很好,但是初一上的半期考试运气比较好数学考了年级前几名,因此被郑老师叫来...

NOIP2010引水入城题解

链接 首先这道题考验的并不是代码能力而是细心程度。仔细读题,你会发现对于每一个城市,如果要建水利设施,必须存在一个与它有公共边的比它高的城市才可以。运用贪心的算法,每次选取最高的靠近湖泊...

[ACM] poj 2369 Permutations (置换群循环节长度)

Description We remind that the permutation of some final set is a one-to-one mapping of the set ont...

POJ2369 Permutations 置换群循环节

题目链接:POJ2369 题目大意:求循环节长度的最小公倍数 如果对这个知识点不是很懂,或者对这个知识点不明白,可以看我之前写的,传送门 /* 2017年8月2日10:10:54 POJ2369...

【poj 3128】置换群 循环节

Leonardo's Notebook Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 185...

POJ3270 Cow Sorting 置换群循环节

题目链接:POJ3270题目大意:给你一组序列,目标为有序序列,每次只能交换两个数,每次的花费为交换的两个值的和,求如何交换使得花费最少。 知识点:置换群循环节 我们知道,当一个循环节中最小的值与这...

[ACM] poj 3128 Leonardo's Notebook (置换群,循环节)

Leonardo's Notebook Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 179...

poj 1026 Cipher(置换群循环节)

Cipher 点击打开题目链接 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 1957...

POJ 1721 CARDS (置换群)

CARDS Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 1920   Accepted...

【POJ 1721】CARDS(置换群)

看箫声漫晕在那抹烟波,淡成了迤逦
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:POJ 1271 CARDS 置换/循环节/置换群开方
举报原因:
原因补充:

(最多只允许输入30个字)