zoukankan      html  css  js  c++  java
  • ### Description

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

      

    Solution

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

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

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

      

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

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

    ​  然后再说一下为什么只需要(1sim 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
    "); return 0;}
        prework(N-10);
        split(n);
        calc();
        ans=dfs(0);
        printf("%d
    ",plu(ans,MOD-1));
    }
    
  • 相关阅读:
    Windows群集安装
    iSCSI配置流程
    StarWind的安装配置
    安装SQL Server 2012 『企业中文版』
    计划安装SQL Server2012需求详细
    Max Degree of Parallelism最大并行度配置
    最佳的MongoDB客户端管理工具
    自学站点地图
    一个解决方案下多个项目时,怎么调试1个项目时不启动其他项目
    如何创建一个https的站点(超简单) 以及 IIS7.5绑定Https域名
  • 原文地址:https://www.cnblogs.com/yoyoball/p/10179432.html
Copyright © 2011-2022 走看看