题意:给你n张卡片, 分给k个人, 从1-n轮流分。 然后,重新把卡片放在一起。第1个人的放在最上边,后面的依次放下面。 再重新分,再放, 问多少次后会回复原样。
如n=10,k=3时的情况 如下
初始 1 2 3 4 5 6 7 8 9 10
1轮 10 7 4 1 8 5 2 9 6 3
2轮 3 2 1 10 9 8 7 6 5 4
3轮 4 7 10 3 6 9 2 5 8 1
4轮 1 2 3 4 5 6 7 8 9 10
其实这个是置换群的一个概念
1 2 3 4 5 6 7 8 9 10
10 7 4 1 8 5 2 9 6 3
其中1->10->3->4->1
2->7->2
3->4->1->10->3
下同
最容易想到的就是枚举 每个数的轮回次数,求最小公倍数,但是超时
优化:如这个轮回1->10->3->4->1,那么其中的数字1,10,3,4一个轮回的次数肯定相同。
所以一旦轮回中有数已经模拟过了,那么直接跳过,因为找出来也和之前的重复。
#include<iostream>
#include<cmath>
using namespace std;
#define LL long long
LL gcd(LL a,LL b)
{
return b==0?a:gcd(b,a%b);
}
LL num[810],v[810];
int main()
{
LL n,k;
while(scanf("%I64d %I64d",&n,&k)&&n+k)
{
LL ans,sum=1;
int t=0;
for(int i=0;i<k&&i<n;i++)
{
for(int j=(n-1-i)/k*k+i;j>=0;j-=k)
num[t++]=j;
}
memset(v,0,sizeof(v));
for(int i=0;i<n;i++)
{
int x=i;
ans=0;
while(!v[x])
{
ans++;
v[x]=1;
x=num[x];
}
if(ans)
sum=sum/gcd(sum,ans)*ans;
printf("%d:%I64d\n",i,ans);
}
printf("%I64d\n",sum);
}
return 0;
}