### Description

  数据范围:\(1<=N<=10^{15}\),保证\(N\)至多有\(6\)个质因子

  

Solution

  数据范围明示要拿质因子搞事而且极有可能是状压qwq

​  那么首先分解一下\(N\)的质因数,然后我们就可以把它的因子按照所含的质因数分类了,含有质因数集合相同的因子归为一类,每一类都可以用二进制压成一个数,具体就是该位为\(0\)表示没有这个质因子,否则表示有,在将\(N\)质因数分解之后,我们就可以算出每一类分别有多少个因数了,若该类的状态为\(st\),记这个值为\(val[st]\)

  继续往状压的方向想,当前的选数情况是否也能压状态呢?答案是肯定的

  

​  根据题意,每个质因子最多出现两次,所以我们可以考虑用这样的一种方式状压:假设\(N\)分解出来有\(m\)个质因子,那么我们考虑将当前的选数状态压成一个\(m+2\)进制的数,每一位如果为\(0\),表示这个质因子没有出现过;如果为\(m+1\)表示这个质因子已经出现在了两个数里面;为\(1\sim m\)表示这个质因子只出现在了一个数里面,并且这样的位置如果有两个的值相同,说明这两个质因子属于同一个数

​  稍微说明一下为什么需要给出现了\(1\)次的质因子标上不同的序号,举一个简单的例子,如果我选了\(21\),那么我还可以选一个\(3\)和一个\(7\),但是如果说我已经选了一个\(3\)和一个\(7\),那么我就不能选\(21\)了,而选了\(21\)的选了\(3,7\)的情况虽然说出现的质因子集合相同,但其实应该看做两种情况,标号就是为了避免这种问题

​  然后再说一下为什么只需要\(1\sim m\)\(m\)个数就可以解决标号的问题:因为每个质因子一旦被两个数包含,直接变成\(m+1\)这种表示”不可以再选“的状态了,所以我们只需要\(m\)个数就可以解决标号问题

​  再来看一下总的状态数:最坏情况下应该是一个\(6\)位的\(8\)进制数,那么总的状态数上限就是\(2^{18}=262144\),问题不大,直接记忆化搜索

  

​  最后是一些实现上的细节:关于那个标号的实现,是用到最小表示法,大概就是说:按照某个顺序扫一遍所有的位置,把我遇到的第一类标为\(1\),第二类标为\(2\),第三类标为\(3\)这样子

​  在这题里面就是:假设当前的状态转化过来的第\(i\)个质因子的标号为\(pre[i]\),当前新加进来的数所属的类的状态为\(st\),我们将加入后的新标号存在\(now[i]\)中,那么我们首先将那些应该被标为\(m+1\)(也就是加入后出现了两次)的质因数在\(now\)中标为\(m+1\),剩下的出现在\(st\)中的位置全部标记为一个新的数字(能跟其他的类区别开来就ok),然后枚举所有的出现过的质因数,开始重标号的过程:如果说这个质因数已经被重标过号了直接跳过,否则将所有原来跟这个质因数标号一致的位置全部标成新的一类(如果那个位置已经在第一轮处理中被标成了\(m+1\)就跳过它)

  

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));
}
posted @ 2018-12-26 15:36  yoyoball  阅读(249)  评论(0编辑  收藏  举报