Description

  數據範圍:\(1<=N<=10^{15}\),保證\(N\)至多有\(6\)個質因子ios

  

Solution

  數據範圍明示要拿質因子搞事並且極有多是狀壓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

  

Code

#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));
}
相關文章
相關標籤/搜索