本文参考于:https://www.cnblogs.com/DreamUp/archive/2010/08/17/1801700.html
传送门:http://poj.org/problem?id=1721
Description
Alice and Bob have a set of N cards labelled with numbers 1 … N (so that no two cards have the same label) and a shuffle machine. We assume that N is an odd integer.
The shuffle machine accepts the set of cards arranged in an arbitrary order and performs the following operation of double shuffle : for all positions i, 1 <= i <= N, if the card at the position i is j and the card at the position j is k, then after the completion of the operation of double shuffle, position i will hold the card k.
Alice and Bob play a game. Alice first writes down all the numbers from 1 to N in some random order: a1, a2, …, aN. Then she arranges the cards so that the position ai holds the card numbered ai+1, for every 1 <= i <= N-1, while the position aN holds the card numbered a1.
This way, cards are put in some order x1, x2, …, xN, where xi is the card at the ith position.
Now she sequentially performs S double shuffles using the shuffle machine described above. After that, the cards are arranged in some final order p1, p2, …, pN which Alice reveals to Bob, together with the number S. Bob’s task is to guess the order x1, x2, …, xN in which Alice originally put the cards just before giving them to the shuffle machine.
Input
The first line of the input contains two integers separated by a single blank character : the odd integer N, 1 <= N <= 1000, the number of cards, and the integer S, 1 <= S <= 1000, the number of double shuffle operations.
The following N lines describe the final order of cards after all the double shuffles have been performed such that for each i, 1 <= i <= N, the (i+1)st line of the input file contains pi (the card at the position i after all double shuffles).
Output
The output should contain N lines which describe the order of cards just before they were given to the shuffle machine.
For each i, 1 <= i <= N, the ith line of the output file should contain xi (the card at the position i before the double shuffles).
Sample Input
7 4
6
3
1
2
4
7
5
Sample Output
4
7
5
6
1
2
3
很显然,这题的一副牌就是一个置换,而每次洗牌就是这个置换的平方运算。由于牌的数量是奇数,平且一开始是一个大循环,所以平方运算的时候不会分裂。所以,在任意时间,牌的顺序所表示的置换一定是一个大循环。
那么根据定理:设Tk = e,(T为一个循环,e为单位循环),那么k的最小正整数解为T的长度。
可以知道,这个循环的n次方为单位循环,换句话说,如果 k mod n = 1,那么这个循环的k次方,就是它本身。我们知道,每次洗牌就是一个简单的平方运算,洗x次就是原循环的2x次方。
因为n是奇数,zx mod n = 1一定是一个<n的整数解,假设这个解是a;那也就是说,一副牌,洗a次,就会回到原先的顺序。使用最终顺序不停地洗,知道回到原先循序,求出循环节长度a后,单纯地向前模拟a-s次,就可以得到原始顺序
时间复杂度 O(n2+log s)。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<ctype.h>
#include<vector>
#include<algorithm>
#include<sstream>
#define PI acos(-1.0)
using namespace std;
typedef long long ll;
typedef unsigned long long llu;
const int inf = 0x3f3f3f3f;
const ll lnf = 0x3f3f3f3f3f3f3f3f;
const int maxn = 2100;
int a[maxn],b[maxn],c[maxn];
int main(void)
{
int n,s;
while(~scanf("%d%d",&n,&s)){
for(int i=1;i<=n;i++){
scanf("%d",a+i);
c[i] = a[i];
}
bool flag = true;
int res = 0;
while(1){ //找循环节长度res
++res;
for(int i=1;i<=n;i++)
b[i] = a[a[i]];
for(int i=1;i<=n;i++){
if(c[i]!=b[i]){
flag = false;
break;
}
}
for(int i=1;i<=n;i++)
a[i] = b[i];
if(flag) break;
flag = true;
}
s = res - s%res; //单纯地向前模拟res-s次,就可以得到原始顺序
for(int i=1;i<=s;i++){
for(int j=1;j<=n;j++)
b[j] = a[a[j]];
for(int j=1;j<=n;j++)
a[j] = b[j];
}
for(int i=1;i<=n;i++)
printf("%d\n",b[i]);
}
return 0;
}
换个方向:给出结果和s以后,可以简单地将这个目标直接置换2s次方。这里有一个技巧,欣慰在开方时只需要再循环中前进2s次,所以我们只关心(2s) mod n,也就避免了大数运算。时间复杂度:O(n+long s)。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<ctype.h>
#include<vector>
#include<algorithm>
#include<sstream>
#define PI acos(-1.0)
using namespace std;
typedef long long ll;
typedef unsigned long long llu;
const int inf = 0x3f3f3f3f;
const ll lnf = 0x3f3f3f3f3f3f3f3f;
const int maxn = 2100;
int a[maxn],b[maxn];
int main(void)
{
int n,s;
while(~scanf("%d%d",&n,&s)){
for(int i=1;i<=n;i++){
scanf("%d",a+i);
}
//将目标循环结果存放在数组b[]中
b[1] = 1;
for(int i=1,j=1;a[j]!=1;){
j = a[j];
b[++i] = j;
}
//求移动步数
int k = 1;
for(int i=1;i<=s;i++)
k = (k*2)%n;
//求开K次方运算后的原始循环存放在数组a[]中
a[1] = b[1];
for(int i=2,j=1;i<=n;i++){
j += k;
if(j>n) j -= n;
a[j] = b[i];
}
//将原始循环转化为原始置换
for(int i=1;i<n;i++)
b[a[i]] = a[i+1];
b[a[n]] = a[1];
for(int i=1;i<=n;i++){
printf("%d\n",b[i]);
}
}
return 0;
}