题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=4259
/*
Author:Bob Lee
2012.8.27
===================================
首先是题目意思的理解问题
有n张牌,然后一次给k个人,多的话就循环着给
然后依次将每个人手中的牌收回来,第一个的放在最上面,并且是倒着的
例如1手中的是1,4,7,10
那么收回去放着的时候就是10,7,4,1
一次往后放,然后再发,再收回来,问你要多少次的循环才能恢复到初始的情况
这个就是题意。
以样例10,3来说
第一次之后为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
我们可以发现一个规律,1它的位置变化是1-4-3-10-1
而4的位置变化为4-3-10-1-4
3的位置变化为3-10-1-4-3
10的位置变化为10-1-4-3-10
不难得出结论这一个循环上的数字的位置变化是一样的,也就是他们要回到自己的初始位置的步数是一样的
那么我们的任务就变成来找出每个这种链的步数,然后求出他们的最小公倍数,这样就得到了结果
这个例子中其它的就是2-7-2和5-6-9-8-5
这个规律我们可以在一次模拟之后就知道了,那么我们首先要做的就是模拟一次,然后通过递推a[data[i]] = i的到我们要得循环链
因为1下一个是4,4后面是10,10后面是3,这样是不是就出来了?
还有一个我们可以发现当n<=k的时候,每个人手中是只有一张牌的,那么收回的时候根本就不会影响到顺序,也就是只要1次就够了,这个就可以加进去优化一下
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define MAXN 1000
long long a[MAXN];
long long data[MAXN];
struct player
{
int count;
int num[MAXN];
}p[MAXN];
bool visit[MAXN];
int n,k;
void init()
{
for(int i=1;i<=n;i++)
{
p[i].count = 0;
visit[i] = false;
data[i] = i;
}
}
void give()//模拟给牌的过程
{
int i;
for(i=1;i<=n;i++)
{
int t = i%k;
if(t==0)
p[k].num[++p[k].count] = data[i];
else
p[t].num[++p[t].count] = data[i];
}
}
void back()//模拟收牌的过程
{
int i,j;
int q = 1;
for(i=1;i<=k;i++)
{
for(j = p[i].count ; j>=1 ;j--)
{
data[q++] = p[i].num[j];
}
}
//由于已经模拟来一次过程,我们就可以找出规律了
for(i=1;i<=n;i++)
{
a[data[i]] = i;
}
}
long long gcd(long long a,long long b)//求最小公约数
{
return a ? gcd(b%a, a) : b;
}
int main()
{
while(scanf("%d%d",&n,&k) && n+k)
{
if(n<=k)//那个优化的过程
{
cout<<1<<endl;
continue;
}
init();
give();
back();
long long ans = 1;
for(int i=1;i<=n;i++)
{
if(visit[i])//如果这个循环节已经访问过来,则跳过
continue;
long long cnt = 0;
int key = i;
while(!visit[key]){
visit[key] = true;
cnt++;
key = a[key];
}
//求出它们的最小公倍数
ans = ans/gcd(ans,cnt)*cnt;
}
cout<<ans<<endl;
}
return 0;
}