数据范围:\(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)); }