本文参考资料:http://hi.baidu.com/bnjyjncwbdbjnzr/item/1f997cfdd225d5d143c36a58
题意:一个生成随机数的函数,
Seed[x+1] = ( seed[x] + STEP ) % MOD
输入step和mod,问能否生成0~MOD-1之间所有的数,是Good Choice,否则Bad Choice
题意其实就是:给出S和M,求0*S%M,1*S%M,2*S%M......(M-1)*S%M能否组成一个集合包含0.1.。。。M-1;(这个是原题意改造而来);
算法:判断两个数是否互质;or 暴力解决
其实暴力完全可以解决这个问题(⊙﹏⊙b),只是其中用数学方法更加高效,巧妙;
下面证明“如果S和M互质则满足题意”:
设G=gcd(S,M);则S=A*G,M=B*G;
另设X=K*S%M=K*S-T*M(T为整数,满足X属于0到M-1。其实就是说T是K*S除以M的商。);
X=K*A*G-T*B*G;因此取余后的整数一定是G的倍数。
所以,上述要求证明的命题其实就是要证明:G只能取1才能满足条件;
(个人感觉上述论证就是废话呵呵——S和M互质不就是gcd(S,M)==1吗?)
(下面继续刚才的证明)
充分性的证明:(即当S与M互质,则0到M-1的S倍对M取余一定能遍历0到M-1)
只需证明的是,该余数中两两之间互不相等;
假设k*S和b*S对M取余相等(k和b∈[0,M),并且k和b不等);
则k*S=q1*M+r≡q2*M+r=b*S <==> (k-b)*S=M*(q1-q2);
因为题设S与M互质,再由上式子可得M|(k-b),这与k和b∈[0,M),并且k和b不等矛盾;
因此题目命题得证。
另外,偶然看到一个很牛叉的辗转相除法;
int gcd(int a,int b)
{
while(b) b^=a^=b^=a%=b;
return a;
}
此代码,很好很强大;把涉及位运算的交换的程序加入,便到得这段简洁高效的代码;
注:A和B;经过A^=B^=A^=B,结果就得到A和B的交换;
1 #include2 long gcd(long a,long b); 3 int main() 4 { 5 long step,mod; 6 while(scanf("%ld%ld",&step,&mod)!=EOF) 7 { 8 if(gcd(step,mod)==1) printf("%10d%10d Good Choice\n\n",step,mod); 9 else printf("%10d%10d Bad Choice\n\n",step,mod);10 }11 return 0;12 }13 long gcd(long a,long b)14 {15 long c;16 if(b==0) return a;17 c=a%b;18 while(c)19 {20 a=b;21 b=c;22 c=a%b;23 }24 return b;25 }
下面是其他网友对该问题的论述:(感觉没有上面的讲的清楚,可以直接忽略)
以数论的观点解释 poj1597 && 更相减损术
http://blog.163.com/jiazheng2222@126/blog/static/1696323832009513104450808/
可以想象成有一个人从起点0处绕着一个圈长为mod个单位“跳步”,每跳一次必须跳step个单位(也就是可不能出现在i*step<pos<(i+1)*step的位置),而至少跳mod次恰好回到起点的话,就是Good Choice,如果在第mod次前就恰好到达起点则为Bad Choice而我们知道,回到起点把所需的最短时间即是step和mod的最小公倍数,而若为Good Choice,显然最短时间又等于step*mod,由数论的知识gcd(step,mod)=step*mod/(step和mod的最小公倍数)=1 bool check(long step,long mod) { long m=step>mod?step:mod; long n=step>mod?mod:step; long q=1; while(q!=0) { q=m-n; m=n>q?n:q; n=n>q?q:n; } if(m==1) return true; else return false; }
http://hi.baidu.com/sjzezoi/item/6a3a71ccab9fee32b67a24fa
题意:一个生成随机数的函数,
Seed[x+1] = ( seed[x] + STEP ) % MOD
问能不能生成0~MOD-1之间所有的数,是Good Choice,否则Bad Choice
此题可以暴力模拟,但正解应该是gcd(STEP,MOD)==1
设G=gcd(a,b),于是有a=A*G,b=B*G(1<=A,B,gcd(A,B)=1) 对于a的多次加可以看成K*a(1<=k),转化成(K*a)%b的所有结果能否表示成0..b-1中的所有数,假(K*a)%b=M,M=K*a-W*b(W为使M>0的最大整数),M=K*A*G-W*B*G M%G==0,既结果是G的倍数,如果想取得0..b-1中的所有数,那么必须G=1才可能..
(代码仅供参考和学习,请不要直接粘贴刷AC数,期待你写出更好的代码)贡献一次PE
暴力:
#include<cstdio>
#include<cstring>
int a,b;
bool vis[100010];
int main()
{
while(scanf("%d%d",&a,&b)+1)
{
memset(vis,0,sizeof vis);
int cnt=0,now=0;
while(cnt<b)
{
if(vis[now]) break;
vis[now]=1;
now=(now+a)%b;
cnt++;
}
if(cnt==b) printf("%10d%10d Good Choice\n\n",a,b);
else printf("%10d%10d Bad Choice\n\n",a,b);
}
return 0;
}
正解:
#include<cstdio>
int a,b;
inline int gcd(int x,int y)
{
if(!y) return x;
return gcd(y,x%y);
}
int main()
{
while(scanf("%d%d",&a,&b)==1)
{
if(gcd(a,b)==1) printf("%10d%10d Good Choice\n\n",a,b);
else printf("%10d%10d Bad Choice\n\n",a,b);
}
return 0;
}
http://hi.baidu.com/zhuangxie1013/item/cc5a130b05e77ad573e676b0
这题其实是道数论题,但我把它当作模拟题来做了,虽然做出来了,但所用的空间和时间也大的吓人,此题输出时需要注意:输出格式为printf("%10d%10d Bad Choice\n\n",step,mod);即第二个数和Bad Choice之间有4个空格,下面是我模拟出来的程序:
#include<iostream> #include<set> #include<algorithm> #include<cstdlib> using namespace std;
int main() { int step,mod,i,temp; set<int>s; while(scanf("%d %d",&step,&mod)!=EOF) { temp=0; s.insert(temp); for(i=1;i<mod;i++) { temp=(temp+step)%mod; s.insert(temp); if(s.size()!=(i+1)) //出现重复元素 { printf("%10d%10d Bad Choice\n\n",step,mod); break; }
} temp=(temp+step)%mod; if(temp==0&&s.size()==mod) printf("%10d%10d Good Choice\n\n",step,mod); s.clear(); } system("pause"); return 0; }
看了别人的方法,讲的都是数论,所以将其转载:
题目的意思就是一个生成随机数的函数,
Seed[x+1] = ( seed[x] + STEP ) % MOD
其中seed就是我们生成出来的随机数,至于seed[0]是哪个数并不重要,后面会证明。STEP就是我们每次往前一个所加的值,再module上MOD得到下一个随机数。
判断这个随机生成函数的好坏的依据是如果能够产生0~MOD-1内的所有数,就是一个好的,否则坏。
我们知道了同余的特性,便可以假设在k步之后,生成的seed[k] = seed[0],所以由此有
Seed[k] = ( seed[0] + STEP*k ) % MOD
那么,
STEP * k = MOD
而我们如果要生产MOD个数,必须使k≥MOD,而且k不可能大于MOD,因为这个条件下生成的数又开始重复,所以k=MOD;在前面的条件下,如果STEP和MOD有大于1的公约数例如d,那么会有STEP*(k/d) = MOD,这就是一个循环了,只会产生k/d<MOD个随机数。
结论:iff gcd(STEP, MOD) == 1, good choice