hdoj 4259 Double Dealing

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4259

1. n张牌 (1,2,···,n) 按顺序发给k个人,再把牌收回,第1个人的放最上面,后面的依次放下面。继续发牌,收牌,问多少次后恢复原样。

2. 以 n=10,k=3 为例,4次后牌的次序恢复原样,如下:

第一次: 10  7  4   1   8   5   2   9   6   3
第二次: 3   2  1   10  9   8   7   6   5   4
第三次: 4   7  10  3   6   9   2   5   8   1
第四次: 1   2  3    4   5   6   7   8   9   10

3. 经过 1 次操作(发牌+收牌)后,下标变化情况如下:

原次序:1  2  3    4  5  6  7  8  9  10
操作后:4  7  10  3  6  9  2  5  8  1

4. 显然,该操作为一个置换,且为:

5. n 元数码上的任意置换 σ 都可唯一地表示成不相交的循环置换的乘积。

6. lcm(x,y)=xy/gcd(x,y).

7. lcm(x1,x2,···,xn)=lcm(lcm(x1,x2,···,xn-1),xn).

8. 本题所求即为各循环置换的循环节的最小公倍数。以 n=10,k=3 为例,所求为 lcm(4,2,4)=4.

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 
 5 __int64 gcd(__int64 x,__int64 y){
 6     return y==0? x:gcd(y,x%y);
 7 }
 8 
 9 int main(){
10     int n,k;
11     while(scanf("%d%d",&n,&k),n||k){
12         int t=n,i,j,f[805];
13         for(i=k;i>=1;i--){  //f[i]:第i张牌经过一次变换后的位置
14             for(j=i;j<=n;j+=k) f[t--]=j;
15         }
16         __int64 ans=1;
17         bool flag[805]={0};
18         for(i=1;i<=n;i++){
19             if(flag[i]) continue;
20             t=f[i];
21             __int64 num=1;
22             while(t!=i){
23                 flag[t]=1;
24                 t=f[t];
25                 num++;
26             }
27             ans=ans/gcd(ans,num)*num;
28         }
29         printf("%I64d\n",ans);
30     }
31     return 0;
32 }

原文地址:https://www.cnblogs.com/linqiuwei/p/3112484.html