2012 ACM/ICPC Asia Regional Online Warmup-1003

/*

http://acm.hdu.edu.cn/showproblem.php?pid=4259


题意是:给定一个排列和一个特定的置换,最后问你最少要多少步置换才能回到原来的状态

解题思路就是:找出置换中的各个置换回路的长度,答案就是各个置换回路的最小公倍数

举个例子:

排列:1 2 3 4 5 6
置换:2 3 5 4 1 6

那么第一个置换回路是1 2 3 5,长度为4,第二个置换回路是 6,长度为1;

所以答案就是4和1的最小公倍数4

而这题的关键就是我们要找到这个置换,也就是初始排列第一次被转换后的结果

假设n为10,k为3;那么这题的初始排列就是1,2,3,4,5,6,7,8,9,10

第一次转换后就是10,7,4,1,8,5,2,9,6,3

第一个回路为:1,10,3,4,长度为4
第二个回路为:2,7,长度为2
第三个回路为:5,8,9,6,长度为4

所以答案就是4,4,2的最小公倍数4

代码如下:

*/

#include<stdio.h>
#include<string.h>
int vis[810],std[810],num[810][810];

__int64 gcd(__int64 a,__int64 b)
{
 return b==0?a:gcd(b,a%b);
}
__int64 solve(int n,int k)
{
 int p,i,j,cnt;
 __int64 sum=1,tem,d;
 for(i=1;i<=k;i++)
 {
  j=i;
  cnt=0;
  while(j<=n)
  {
   num[i][++cnt]=j;
   j+=k;
  }
  num[i][0]=cnt;
 }
 cnt=0;
 for(i=1;i<=k;i++)
 {
  for(j=num[i][0];j>=1;j--)
   std[++cnt]=num[i][j];
 }
 memset(vis,0,sizeof(vis));
 for(i=1;i<=n;i++)
 {
  if(!vis[i])
  {
   tem=1;
   vis[i]=1;
   p=i;
   while(std[p]!=i)
    ++tem,p=std[p],vis[p]=1;
   d=gcd(sum,tem);
   sum/=d;
   sum*=tem;
  }
 }
 return sum;
}
int main()
{
 int n,k;
 while(scanf("%d%d",&n,&k),n+k)
  printf("%I64d\n",solve(n,k));
 return 0;
}


 

你可能感兴趣的:(2012 ACM/ICPC Asia Regional Online Warmup-1003)