811160Sample OutputCase 1: 1Case 2: 2Case 3: 0题意:
给出一个数L(L<2,000,000,000),找一个最小的L的倍数,使其乘积的每个数,字都是8,求这个乘积的位数。
设此乘积位数为x,倍数为k
则有
(10 ^ x - 1) * 8 / 9 = k * L
10 ^ x - 1 = 9 / 8 * k * L
令 m = 9 * L / gcd (L , 8), y = k * gcd (L , 8) / 8
若此L有解,则
1:满足上式
2:k为整数
3:y为整数
假设1,2成立,则3若成立,L即有解
则有
(10 ^ x - 1) % m = 0
10 ^ x % m = 1
由欧拉公式得: 若上式有解,则必有10与m互素,且x = φ(m)
φ(m)肯定为L有解的一个倍数,但不一定是最小的,x的最小值肯定为φ(m)的约数
于是穷举约数检查是否符合10 ^ x % m = 1,找到最小x
注意:
1:枚举时只枚举到sqrt(φ(m))再倒回去通过前半段算出后半段即可,节省时间
2:此题检验时由于是10的指数,容易溢出,所以快速幂相乘时改成了加法做乘法,倍增加法,原理同倍增快速幂
Code:
| Status | Accepted |
|---|---|
| Time | 156ms |
| Memory | 1728kB |
| Length | 1063 |
| Lang | C++ |
| Submitted | 2017-02-07 17:20:08 |
| Shared |
#include<iostream>#include<cstdio>#include<cmath>using namespace std;typedef long long LL;LL gcd(LL A,LL B){return B==0?A:gcd(B,A%B);}LL mul(LL a, LL b, LL mod){//考虑换成m个10相加,倍增+mod LL rt=0; while(b){ if(b&1) rt=(rt+a)%mod; a=(a<<1)%mod; b>>=1; } return rt;}LL qmul(LL a, LL b, LL mod){ LL s=1,tmp=a; while(b){ if(b&1) s=mul(s,tmp,mod); tmp=mul(tmp,tmp,mod); b>>=1; } return s;}LL Euler(LL n){ LL m=(int)sqrt(n+0.5); LL rt=n; for(LL i=2; i<=m; ++i)if(n%i==0){ rt=rt/i*(i-1); while(n%i==0)n/=i; } if(n>1)rt=rt/n*(n-1); return rt;}int main(){ int tot=0,d; LL L,m; while(scanf("%I64d",&L)&&L){ m=9*L/gcd(L,8); printf("Case %d: ",++tot); d=gcd(10,m); if(d==1){ LL phi=Euler(m); LL ans=phi; LL p=sqrt((double)phi); bool kk=0; for(int i=1; i<=p; ++i)if(phi%i==0&&qmul(10,i,m)==1){ ans=i,kk=1; break; } if(!kk){ for(int i=p; i>=2; --i)if(phi%i==0&&qmul(10,phi/i,m)==1){ ans=phi/i,kk=1; break; } } printf("%I64d/n",ans); } else {printf("0/n");} }}
新闻热点
疑难解答