# 演算法課程題解 - 基礎數論 3 # TOJ 442 # UVa 516 ## 題目 http://domen111.github.io/UVa-Easy-Viewer/?516 給一個數字 $X$ 質因數分解後式,求 $X-1$ 的質因數分解式 ## 解法 By Koios1143 ### 想法 可以利用 stringstream 讀取輸入,取得所有數值就可以拿到 $X$ 以及 $X'=X-1$ 至於質因數分解的部分,我們可以藉由質數篩的過程當中紀錄每個數字第一次是被誰篩掉(也就是最小因數) 例如: `10 -> 2` `9 -> 3` `17 -> 17` 如此一來,對於每個詢問 $X'$ 每次先找到最小因數 $p$ ,紀錄當前的值 $X'$ 可以被整除幾次,並且將 $X'$ 除以 $p$,直到 $X'$ 為 $1$ 的時候表示我們已經找完了 ### 程式碼 ```cpp= //By Koios1143 #include<iostream> #include<sstream> #include<cmath> const int MaxN = 32768; int fact[MaxN], ans[MaxN][2]; using namespace std; string s; bool fir; int x,n,a,b,p,cnt,px; int main(){ // 質數篩 for(long long i=2 ; i<=MaxN ; i++){ if(!fact[i]){ fact[i]=i; for(long long j=i*i ; j<=MaxN ; j+=i){ if(!fact[j]){ fact[j]=i; } } } } while(getline(cin,s)){ if(s=="0") break; fir=true; n=1; px=0; // 計算 X stringstream ss; ss<<s; while(ss>>x){ if(fir) a=x; else{ b=x; n*=pow(a,b); } fir=!fir; } n--; // 質因數分解 while(n!=1){ p=fact[n]; cnt=0; while(n%p==0){ cnt++; n/=p; } ans[px][0]=p; ans[px][1]=cnt; px++; } for(int i=px-1 ; i>=0 ; i--){ if(i!=px-1) cout<<" "; cout<<ans[i][0]<<" "<<ans[i][1]; } cout<<"\n"; } return 0; } ``` ### 複雜度分析 質數篩的複雜度為 $O(NloglogN)$ 每筆測資計算 $X$ 時間複雜度約為 $O(\omega(X))$ 每筆測資質因數分解時間複雜度約為 $O(\Omega(X))$ 每筆測資輸出時間複雜度約為 $O(\omega(X))$ 其中, $\omega(X)$ 表示 $X$ 的不同質因數個數。$\Omega(X)$ 表示 $X$ 的質因數個數 總時間複雜度約為 $O(NloglogN + 2\omega(X) + \Omega(X))$ # UVa 10699 ## 題目 http://domen111.github.io/UVa-Easy-Viewer/?10699 多筆輸入,每筆輸入包含一個正整數 $X$ , 求 $X$ 有多少不同的質因數 ## 解法 By Koios1143 ### 想法 跟上一題類似,先透過質數篩找到每個數的最小質因數 $p$ ,再透過不斷的除以 $p$ 直到剩下 $1$ 為止,中間除去重複的以外,剩下就是答案 ### 程式碼 ```cpp= //By Koios1143 #include<iostream> const int MaxN = 1e6+5; int fact[MaxN]; using namespace std; int n,cnt,p; int main(){ for(long long i=2 ; i<=MaxN ; i++){ if(!fact[i]){ fact[i]=i; for(long long j=i*i ; j<=MaxN ; j+=i){ if(!fact[j]){ fact[j]=i; } } } } while(cin>>n && n){ cnt=0; cout<<n<<" : "; while(n!=1){ p=fact[n]; cnt++; while(n%p==0){ n/=p; } } cout<<cnt<<"\n"; } return 0; } ``` ### 複雜度分析 質數篩的複雜度為 $O(NloglogN)$ 每筆測資質因數分解時間複雜度約為 $O(\Omega(X))$ 也有人說是約為 $O(logX)$ 總時間複雜度約為 $O(NloglogN + \Omega(X))$ # UVa 10789 ## 題目 http://domen111.github.io/UVa-Easy-Viewer/?10789 給一串字串 $S$ ,統計每個字元出現的次數,並輸出次數為質數的字元 ## 解法 By Koios1143 ### 想法 先建立質數表,統計過後查表即可 ### 程式碼 ```cpp= //By Koios1143 #include<iostream> using namespace std; const int MaxN = 2002; bool prime[MaxN]; string s; int n,tbl[128]; string crt="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; int main(){ for(int i=0 ; i<=MaxN ; i++){ prime[i]=true; } prime[0] = prime[1] = false; for(long long i=2 ; i<=MaxN ; i++){ if(prime[i]){ for(long long j=i*i ; j<=MaxN ; j+=i){ prime[j]=false; } } } cin>>n; for(int Case=1 ; Case<=n ; Case++){ cin>>s; cout<<"Case "<<Case<<": "; for(int i=0 ; i<128 ; i++){ tbl[i]=0; } for(int i=0 ; i<s.size() ; i++){ tbl[s[i]]++; } bool out=false; for(int i=0 ; i<crt.size() ; i++){ if(prime[tbl[crt[i]]] == true){ cout<<crt[i]; out=true; } } if(!out){ cout<<"empty"; } cout<<"\n"; } return 0; } ``` ### 複雜度分析 建表時間複雜度為 $O(NloglogN)$ 統計時間複雜度為 $O(len(S))$ 總時間複雜度為 $O(NloglogN + len(S))$ # UVa 11960 ## 題目 http://domen111.github.io/UVa-Easy-Viewer/?11960 多筆測資,每筆測資包含一個正整數 $X$ ,求所有 $<=X$ 的正整數當中,因數個數最多的最大整數為何 ## 解法 By Koios1143 ### 想法 先做質因數分解,並且透過以下的想法可以找出每個數的因數個數 我們可以觀察 $24 = 2^3 * 3$ 其中的因數包含了 $1, 2, 3, 4, 6, 8, 12, 24$ 觀察一下會發現,這些因數都是由 $2$ 的某個小餘等於 $3$ 的次方乘上 $3$ 的某個小於等於 $1$ 的次方組合而成 也就是說我可以選擇要拿多少 $2$ 以及要拿多少 $3$ ,因數個數為 $(3+1)*(1+1)=8$ 所以我們得到若 $X=a^p+b^q+c^r$ ,則 $X$ 的因數個數為 $(p-1)*(q-1)*(r-1)$ 話說回來,利用上述的性質,加上質因數分解,就可以輕鬆求出每個數值的因數個數 而由於題目只需要所有 $<=X$ 的正整數當中,因數個數最多的最大整數,所以我們可以預先處理好所有人的答案 ### 程式碼 ```cpp= //By Koios1143 #include<iostream> const int MaxN = 1e6+5; int fact[MaxN],ans[MaxN]; using namespace std; int t,x; int main(){ for(long long i=2 ; i<=MaxN ; i++){ if(!fact[i]){ fact[i]=i; for(long long j=i*i ; j<=MaxN ; j+=i){ if(!fact[j]){ fact[j]=i; } } } } for(int i=1 ; i<=MaxN ; i++){ // 後面要做乘法,所以預設值為 1 ans[i]=1; } for(int i=2 ; i<=MaxN ; i++){ int tmp=i; while(tmp!=1){ int p=fact[tmp], cnt=0; while(tmp%p==0){ tmp/=fact[tmp]; cnt++; } ans[i]*=(cnt+1); } } // 預處理答案 int n=2,m=ans[2]; for(int i=2 ; i<=MaxN ; i++){ if(ans[i]>=m){ m=ans[i]; n=i; } ans[i]=n; } cin>>t; while(t--){ cin>>x; cout<<ans[x]<<"\n"; } return 0; } ``` ### 複雜度分析 質數篩的複雜度為 $O(NloglogN)$ 對所有數值做質因數分解,時間複雜度約為 $O(Nlog\Omega(X))$ 預處理答案的時間複雜度為 $O(N)$ 每筆回答時間複雜度為 $O(1)$ 總時間複雜度為 $O(NloglogN + Nlog\Omega(X) + O(N) + O(t))$ ###### tags: `SCIST 演算法 題解`