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 }
抓住青春的尾巴。。。