poj-3696 The Luckiest number
阿新 • • 發佈:2017-06-29
ret 全部 -a using poj spa ans 乘法 bre
題意:
給出一個數L。求一個最小的x。使長度為x的888...8這個數整除L。
無解輸出0,L<=2*10^9;
題解:
即求滿足下式的最小x值:
8/9*(10^x-1)==k*L (k為正整數)
8*(10^x-1)==k*9*L
為繼續化簡,求出r=gcd(L,8)。
8/r *(10^x-1)==k*9*L/r
由於8/r與9*L/r互質。9*L這個因式必在(10^x-1)中,所以原式即為:
10^x-1≡0(mod 9*L/r)
10^x≡1(mod 9*L/r)
設q=9*L/r。由歐拉定理得;
10^φ(q)≡1(mod q)
當gcd(q,10)==1時成立。不等於1時無解;
那麽φ(q)就是滿足題意的的一個解。
但這個解未必是最小的;
實際上。這裏的答案也就是10對模q的指數,暫且記為ans;
有定理說明了對隨意的d滿足10^d≡1(mod q)時,d mod ans==0。
所以φ(q) mod ans == 0。
由於φ(q)可能非常大,不能直接枚舉ans驗證,就將φ(q)分解因式;
對全部的質因子i去驗證φ(q)/i是否滿足10^φ(q)/i≡1(mod q)。
一直枚舉完質因子,最後得到的就是ans。
程序實現方面。gcd沒什麽問題;
為了更快的分解因數(φ函數和最後求解都要用),能夠預處理一個素數表(大小到10^5就夠了)。
驗證模線性方程要用到高速冪,可是由於取模數在10^10量級,乘法時有溢出long long的可能。
所以高速冪要套個高速乘。也是一樣取模q;
求解上面說了,不再贅述。
代碼:
#include<stdio.h> #include<string.h> #include<algorithm> #define N 1000001 using namespace std; typedef long long ll; ll pri[N],tot; bool vis[N]; void init() { for(ll i=2;i<N;i++) { if(!vis[i]) pri[++tot]=i; for(ll j=1;j<=tot&&i*pri[j]<N;j++) { vis[i*pri[j]]=1; if(i%pri[j]==0) break; } } } ll gcd(ll a,ll b) { ll t=a%b; while(t) { a=b,b=t; t=a%b; } return b; } ll mul(ll x,ll y,ll mod) { ll ret=0; while(y) { if(y&1) ret=(ret+x)%mod; x=(x+x)%mod; y>>=1; } return ret; } ll pow(ll x,ll y,ll mod) { ll ret=1; while(y) { if(y&1) ret=mul(ret,x,mod); x=mul(x,x,mod); y>>=1; } return ret; } ll eular(ll x) { ll ret=x,i; for(i=1;pri[i]*pri[i]<=x;i++) { if(x%pri[i]==0) { while(x%pri[i]==0) x/=pri[i]; ret/=pri[i],ret*=pri[i]-1; } } if(x!=1) ret/=x,ret*=x-1; return ret; } ll Ans(ll q) { if(gcd(q,10)!=1) return 0; ll ret=eular(q),temp=ret,cnt; for(ll i=1;pri[i]*pri[i]<=temp;i++) { if(temp%pri[i]==0) { cnt=0; while(temp%pri[i]==0) cnt++,temp/=pri[i]; while(cnt) { if(pow(10,ret/pri[i],q)==1) { ret/=pri[i],cnt--; } else break; } } } if(temp!=1) if(pow(10,ret/temp,q)==1) ret/=temp; return ret; } int main() { int c=1; ll n,i,j,k,p,q; init(); while(scanf("%lld",&n)&&n) { q=9*n/gcd(n,8); printf("Case %d: %lld\n",c++,Ans(q)); } return 0; }
poj-3696 The Luckiest number