1077: 循环节长度

时间限制: 1 Sec  内存限制: 128 MB
提交: 103  解决: 37
[提交][状态][讨论版]

题目描述

数一有很多的有理数,然而有的是有限小数,如1/2=0.5,1/5=0.2,这些都很好写,但是对于无限循环小数,数一就发愁了,怎么写得完啊。于是数一就想让你告诉他这些有理数的循环节长度是多少。

 

输入

多组数据,每组数据是两个整数p,q(0<=p<10^9,0<q<=10^9),表示一个有理数的分子与分母。

 

输出

若为有限小数,则输出0,若为无限循环小数,请输出循环节长度。

 

样例输入

1 2
1 3
2 10
2 7
5 6

样例输出

0 1 0 6 1

想法:

  1 证明有理数a/b可以写成无限循环小数

   1) 根据抽屉原理 存在10^m%b=10^n%b

      对于任何b存在一个数c (b|c 且c可以写成10^i(10^j-1)的形式 )

 2)a/b=d/c [ d=a*c/b   则d是整数 ]=(d/10^i) * ( 1/(10^j-1) )

 3)   x=  1/(10^j-1)  一定是有理小数并且循环节长度为j

  方法

  1  用bsgs 求解(10^j%b=1)

   2 用数组实现hash

代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 const int mod=774321;
 5 struct node {
 6     int k1,k2;
 7     int _next;    
 8 };
 9 int hs[mod+7];
10 node hn[mod+7]; int cnt;
11 int gcd (int x,int y) {
12     return !y?x:gcd(y,x%y);
13 }
14 void  _add (int x, int key) {
15     int head=x%mod;
16     hn[++cnt].k2=key; hn[cnt].k1=x;
17     hn[cnt]._next=hs[head];
18     hs[head]=cnt;
19 }
20 int _find (int x) {
21     int head=x%mod;
22     for (int i=hs[head];i!=-1;i=hn[i]._next) {
23         if (hn[i].k1==x)
24             return hn[i].k2;
25     }
26     return -1;
27 }
28 int bsgs (int k,int p) { 
29     cnt=0; memset (hs,-1,sizeof(hs));
30     LL x=1; int t=sqrt(p)+1;  
31     for (int i=0;i<t;i++) {
32         _add (x,i);
33         x=x*k%p;
34     }
35     LL y=1; 
36     for (int i=1;i<=t;i++) {
37         y=y*x%p;  // 不使用long long 会溢出
38         int ans=_find(y);
39         if (ans>=0)  return i*t-ans;
40     }
41 }
42 int main ()
43 {
44     int a,b; 
45     while ( ~scanf ("%d %d",&a,&b) ) {
46         int k=gcd (a,b); a/=k; b/=k;
47         while (b%2==0) b/=2;
48         while (b%5==0) b/=5;
49         if (b==1) printf("0\n");
50         else      printf("%d\n",bsgs(10,b) );
51     }
52     return 0;
53 }