题目链接: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 }