Vjudge
你有一個數\(n\),\(n\le 10^{24}\),爲了方便會告訴你\(n\)分解以後有\(m\)個不一樣的質因子,而且把這些質因子給你。
你每次能夠把\(n\)變成一個它的約數,求變成\(1\)的指望步數。ios
首先暴力的轉移是:
\[f[n]=1+\frac{1}{\sigma(n)}\sum_{d|n}f[d]\]
不難發現這個狀態之和每一個質因子的出現次數的集合相關,與質因子是什麼無關。
發現\(n\)本質不一樣的質因子最多隻有\(18\)個,那麼咱們爆搜這個每一個質因子出現次數的集合,強制較小的質因子出現次數較大,搜完以後發現狀態只有\(172513\)個。
因而咱們對於每一個\(n\)的質因子出現個數的集合計算答案,只須要求解一個高維前綴和就能夠進行轉移了。
這裏高維前綴和的求法,設\(g[n][j]\)表示對於\(n\)這個數(這個數是爆搜出來的,也就是知足小的質因子的出現次數不會少於大的質因子的出現次數),其前\(j\)個質因子的出現次數都相同,可是\(j\)以後的質因子出現次數小於等於當前位置的全部\(f[n]\)的和,轉移的時候枚舉給哪一位減一就好了。spa
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> using namespace std; #define ll __int128 #define MAX 200200 const ll Limit=(ll)1e12*(ll)1e12; ll n;int m,Case; char ch[30];int a[30]; int p[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73}; map<ll,int> M;int tot;ll val[MAX]; double g[MAX][20],f[MAX]; void dfs(int x,int lst,ll s) { val[M[s]=++tot]=s;s*=p[x]; for(int i=1;i<=lst&&s<=Limit;++i,s*=p[x])dfs(x+1,i,s); } int main() { dfs(0,90,1); for(int i=2;i<=tot;++i) { ll x=val[i];for(int j=0;j<18;++j)a[j]=0; for(int j=0;j<18;++j)while(x%p[j]==0)++a[j],x/=p[j]; for(int j=17;~j;--j) if(a[j]) { int k=j;while(k<17&&a[k+1]==a[j])++k; g[i][j]=g[i][j+1]+g[M[val[i]/p[k]]][j]; } int tmp=1; for(int j=0;j<18;++j)tmp*=a[j]+1; f[i]=(g[i][0]+tmp)/(tmp-1); for(int j=0;j<18;++j)g[i][j]+=f[i]; } while(scanf("%s",ch+1)!=EOF) { for(int i=1,l=strlen(ch+1);i<=l;++i)n=n*10+ch[i]-48; scanf("%d",&m); for(int i=0;i<m;++i) { int p;scanf("%d",&p);a[i]=0; while(n%p==0)n/=p,++a[i]; } sort(&a[0],&a[m]);reverse(&a[0],&a[m]); for(int i=0;i<m;++i) for(int j=1;j<=a[i];++j) n*=p[i]; printf("Case #%d: %.10lf\n",++Case,f[M[n]]); for(int i=0;i<m;++i)a[i]=0;n=0; } return 0; }