ZOJ Monthly, January 2010
❤思路
场上快结束的时候才想到的。
只需要进行操作一次就可以了。就能够找到循环关系。
比如样例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
4 5 6 1 7 8 9 2 10 11 12 3 13 14 15
循环:
1 - 4;
2 - 5 - 7 - 9 - 10 - 11 - 12 - 3 - 6 - 8 ;
13 - 13;
14 - 14;
15 - 15;
a 数组存的是操作一次后的答案。
从1-n for一下,然后 ma[i]=0 说明第i个没有在任何循环里。然后开始以此为新循环的头开始查找循环。存在 b 数组里。每一个循环查找完毕后,更新 c 数据,即 ans 数组。根据这个循环里,第 i 个数的位置 i 来查找答案。mark=(i+p)%len(len为该循环的长度)即是操作 p 次后循环到哪个数了。
#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
int a[50100],b[50100];
int c[50100];
int ma[50100];
int n,m,p,ax,bx;
int main(){
int mark;
int len;
while(scanf("%d%d%d%d%d",&n,&m,&p,&ax,&bx)!=EOF){
mark=n/m;
memset(ma,0,sizeof(ma));
int i,j;
j=1;
for(i=1;i<=mark;i++){
a[m*i]=j;
//printf("m*i %d j %d\n",m*i,j);
j++;
}
j=n/m+1;
for(i=1;i<=n;i++){
if(i%m!=0){
a[i]=j;
j++;
}
}
for(i=1;i<=n;i++){
if(ma[i]==0){
len=0;
b[len++]=i;
for(j=0;j<len;j++){
if(a[b[j]]==b[0]) break;
b[len++]=a[b[j]];
}
for(j=0;j<len;j++){
ma[b[j]]=1;
mark=(j+p)%len;
c[b[j]]=b[mark];
}
}
}
for(i=ax;i<=bx;i++){
if(i!=bx)
printf("%d ",c[i]);
else
printf("%d\n",c[i]);
}
}
return 0;
}
如果 i 是 m 的倍数,则 a[i]=j++;(初始化 j =1;)
否则,a[i]=p++;(初始化 p =n/m+1;)
❤
其实中间有一段是跟着大牛学习的。觉得那段更新特别好。
for(i=1;i<=n;i++){
if(ma[i]==0){
len=0;
b[len++]=i;
for(j=0;j<len;j++){
if(a[b[j]]==b[0]) break;
b[len++]=a[b[j]];
}
……
}
在这段里,len是不断++的。只要指向的下一个不指向环的开头,在存的过程中,就把 len++了。这样 for 循环的条件,在下一次的时候也不停止,因为 j++ 的同时,len 也变大了。
学习一下。