數據範圍:\(1<=N<=10^{15}\),保證\(N\)至多有\(6\)個質因子ios
數據範圍明示要拿質因子搞事並且極有多是狀壓qwqc++
那麼首先分解一下\(N\)的質因數,而後咱們就能夠把它的因子按照所含的質因數分類了,含有質因數集合相同的因子歸爲一類,每一類均可以用二進制壓成一個數,具體就是該位爲\(0\)表示沒有這個質因子,不然表示有,在將\(N\)質因數分解以後,咱們就能夠算出每一類分別有多少個因數了,若該類的狀態爲\(st\),記這個值爲\(val[st]\)spa
繼續往狀壓的方向想,當前的選數狀況是否也能壓狀態呢?答案是確定的code
根據題意,每一個質因子最多出現兩次,因此咱們能夠考慮用這樣的一種方式狀壓:假設\(N\)分解出來有\(m\)個質因子,那麼咱們考慮將當前的選數狀態壓成一個\(m+2\)進制的數,每一位若是爲\(0\),表示這個質因子沒有出現過;若是爲\(m+1\)表示這個質因子已經出如今了兩個數裏面;爲\(1\sim m\)表示這個質因子只出如今了一個數裏面,而且這樣的位置若是有兩個的值相同,說明這兩個質因子屬於同一個數blog
稍微說明一下爲何須要給出現了\(1\)次的質因子標上不一樣的序號,舉一個簡單的例子,若是我選了\(21\),那麼我還能夠選一個\(3\)和一個\(7\),可是若是說我已經選了一個\(3\)和一個\(7\),那麼我就不能選\(21\)了,而選了\(21\)的選了\(3,7\)的狀況雖說出現的質因子集合相同,但其實應該看作兩種狀況,標號就是爲了不這種問題ip
而後再說一下爲何只須要\(1\sim m\)這\(m\)個數就能夠解決標號的問題:由於每一個質因子一旦被兩個數包含,直接變成\(m+1\)這種表示」不能夠再選「的狀態了,因此咱們只須要\(m\)個數就能夠解決標號問題rem
再來看一下總的狀態數:最壞狀況下應該是一個\(6\)位的\(8\)進制數,那麼總的狀態數上限就是\(2^{18}=262144\),問題不大,直接記憶化搜索get
最後是一些實現上的細節:關於那個標號的實現,是用到最小表示法,大概就是說:按照某個順序掃一遍全部的位置,把我遇到的第一類標爲\(1\),第二類標爲\(2\),第三類標爲\(3\)這樣子string
在這題裏面就是:假設當前的狀態轉化過來的第\(i\)個質因子的標號爲\(pre[i]\),當前新加進來的數所屬的類的狀態爲\(st\),咱們將加入後的新標號存在\(now[i]\)中,那麼咱們首先將那些應該被標爲\(m+1\)(也就是加入後出現了兩次)的質因數在\(now\)中標爲\(m+1\),剩下的出如今\(st\)中的位置所有標記爲一個新的數字(能跟其餘的類區別開來就ok),而後枚舉全部的出現過的質因數,開始重標號的過程:若是說這個質因數已經被重標過號了直接跳過,不然將全部原來跟這個質因數標號一致的位置所有標成新的一類(若是那個位置已經在第一輪處理中被標成了\(m+1\)就跳過它)it
#include<iostream> #include<cstdio> #include<cstring> #define ll long long #define ban (lis[0]+1) using namespace std; const int N=6e7+10,ST=(1<<6)+10,MOD=1e9+7; int p[6000010],vis[N]; int pw[10]; ll rec[10],lis[10]; int f[1<<18],calced[1<<18]; int val[(1<<6)+10]; int cnt,ans,all,allst; ll n; int plu(int x,int y){return (1LL*x+y)%MOD;} int mul(int x,int y){return 1LL*x*y%MOD;} int St(int x){return 1<<x-1;} int in(int st,int x){return st>>x-1&1;} void prework(int n){ for (int i=2;i<=n;++i){ if (!vis[i]) p[++cnt]=i; for (int j=1;j<=cnt&&i*p[j]<=n;++j){ vis[i*p[j]]=1; if (i%p[j]==0) break; } } } void split(ll x){ lis[0]=0; for (int i=1;1LL*p[i]*p[i]<=x&&x>1&&i<=cnt;++i){ if (x%p[i]) continue; lis[++lis[0]]=p[i]; while (x%p[i]==0) x/=p[i],++pw[lis[0]]; } if (x>1){ lis[++lis[0]]=x; pw[lis[0]]=1; } } void calc(){ for (int st=1;st<1<<lis[0];++st){ val[st]=1; for (int i=1;i<=lis[0];++i) if (in(st,i)) val[st]=mul(val[st],pw[i]); } } void get_val(int st,int *rec){ for (int i=1;i<=lis[0];++i){ rec[i]=st%(lis[0]+2); st/=lis[0]+2; } } void get_st(int &st,int *rec){ st=0; for (int i=lis[0];i>=1;--i) st=st*(lis[0]+2)+rec[i]; } bool ok(int *now,int st){ bool vis[10]; memset(vis,0,sizeof(vis)); int cnt=0; for (int i=1;i<=lis[0];++i){ if (!in(st,i)) continue; if (now[i]==ban) return 0; if (now[i]&&!vis[now[i]]){ ++cnt; vis[now[i]]=1; } } return cnt<2; } void trans(int *pre,int st,int *now){ for (int i=1;i<=lis[0];++i) now[i]=pre[i]; for (int i=1;i<=lis[0];++i) if (in(st,i)){ if (pre[i]) now[i]=ban; else now[i]=-1;//wait to mark } int id[10],cnt=0,tmp; memset(id,0,sizeof(id)); for (int i=1;i<=lis[0];++i){ if (now[i]==ban||id[i]||!now[i]) continue; ++cnt; tmp=now[i]; for (int j=i;j<=lis[0];++j) if (now[j]==tmp&&!id[j]){ id[j]=cnt; now[j]=cnt;//remark } } } int dfs(int nowst){ if (calced[nowst]) return f[nowst]; int now[10],nxt[10]; get_val(nowst,now); int ret=1,tmp; for (int st=1;st<(1<<lis[0]);++st){ if (!ok(now,st)) continue; trans(now,st,nxt); get_st(tmp,nxt); ret=plu(ret,mul(val[st],dfs(tmp))); } calced[nowst]=1; f[nowst]=ret; return ret; } int main(){ #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); #endif scanf("%lld",&n); if (n==1){printf("0\n"); return 0;} prework(N-10); split(n); calc(); ans=dfs(0); printf("%d\n",plu(ans,MOD-1)); }