假設某個數 n 的質因數分解為 $p_1^{a_1} \cdot ... \cdot p_k^{a_k}$ ,那麼首先不難得到那個函數的值其實就是 $( 1 + p_1^{a_1} ) \cdot ... \cdot ( 1 + p_k^{a_k} )$ ,只要把這條式子展開獲得的每一項就都是一個滿足條件的 $n$ 的因數。因此問題轉化為有幾種方法可以把 $A$ 表成上述的型式。我的方法是先遇處理出所有 $A$ 的因數裡面形如 $1 + p^x$ 的數(以下簡稱這種數是「好的」),找 $A$ 的因數就只要用單純的 $\sqrt{A}$ 的方法就可以了,至於找到一個因數時要如何判斷他是否是好的 ,我的作法是對於每個小於 $10^6$ 的質數 $p$ ,先處理好由他形成的好數有哪些(因為他們不會太多),這樣在判斷一個數是否為好數時,首先看他有沒有出現在前面遇處理好的好數裡,有的話就代表有,而沒有的話則還有一種可能,就是他是由 $> 10^6$ 的質數形成的好數,但此時這個質數平方就會超過 $10^{12}$ 了,因此他只有可能是質數$+1$,因此這時候只要判斷詢問的這個數減一是不是質數就可以了。對於所有找到的好數,用一個 map<int,vector<int> > 把他們裝起來,其中由同一個質數形成的好數放在同一個 vector 裡面(因此前面遇處理好數們的時候必須用 map ,多紀錄這個好數是由哪個質數構成的,查詢時直接回傳即可),於是此時問題就簡化為:現在有很多個 vector ,問從不同 vector 取出一些數讓他們乘起來等於 $A$ 的方法數有幾種。
這時再多觀察到一件事:如果我們想用一些數來乘出 $A$ ,那麼中間乘到一半的數們一定都會是 $A$ 的因數,所以這個問題就可以用DP處理了!記 $dp[ i ][ j ]$ 代表當只考慮第 $1$ ~ $i$ 個 vector 時,有幾種方法可以乘出 $j $,其中 $j$ 是 $A$ 的一個因數。邊界為 $dp[ 0 ] [ 1 ] = 1$ ,轉移算是顯然的,並且他可以輕鬆的壓成一維的 DP 。另外要注意的是,這邊的 dp 表格其實是一個 map ,因為在 $j$ 的部份會有很大的數。可以得到這個算法的複雜度為 $O(K^2 log K)$ ,其中 $K$ 是 $A$ 的因數數量,而在 $A \leq 10^{12}$ 時 $K$ 不會超過 $7000$ ,所以還不至於 TLE 。
另外我看到的最短的code是直接DFS,比我的作法簡單多了QQ,也很好理解,不過似乎不保證複雜度就是了,時間 1887ms / 2s 也是很驚險的秒數。
code :
#include<bits/stdc++.h> #define INF 1000000000000LL #define LL long long #define F first #define S second using namespace std; const int maxn=1000000+10 ; bool vis[maxn] ; int p[maxn],pcnt=0 ; void prime() { for(int i=2;i*i<maxn;i++) if(!vis[i]) for(int j=i*i;j<maxn;j+=i) vis[j]=1 ; for(int i=2;i<maxn;i++) if(!vis[i]) p[++pcnt]=i ; } bool isp(LL x) { if(x<maxn) return !vis[x] ; for(int i=1;i<=pcnt;i++) if((x%p[i])==0) return 0 ; return 1 ; } map<LL,LL> good ; map<LL,vector<LL> > mp ; LL isgood(LL x) { auto it=good.find(x) ; if(it!=good.end()) return it->S ; return (x>=maxn && isp(x-1)) ? x-1 : 0 ; } LL n ; LL di[maxn] ; int dcnt ; map<LL,LL> dp ; main() { prime() ; for(int i=1;i<=pcnt;i++) for(LL j=p[i];j<=INF;j*=p[i]) good[j+1]=p[i] ; scanf("%lld",&n) ; for(LL i=1;i*i<=n;i++) if((n%i)==0) { di[dcnt++]=i ; LL x=isgood(i) ; if(x) mp[x].push_back(i) ; if(i*i==n) continue ; di[dcnt++]=n/i ; x=isgood(n/i) ; if(x) mp[x].push_back(n/i) ; } sort(di,di+dcnt) ; dp[1]=1 ; for(auto it : mp) for(int i=dcnt-1;i>=0;i--) for(auto j : it.S) if(di[i]%j==0) dp[di[i]]+=dp[di[i]/j] ; printf("%lld\n",dp[n]) ; }
沒有留言:
張貼留言