JZOJ 3775. 【NOIP2014模拟8.15】因子的排列

题目

题目描述
一天,小B学习了分解质因数的相关内容。他发现,一个数的质因子可以有许多不同的排列方式,例如20=2*2*5=2*5*2=5*2*2,那么小B认为20的质因子有3种不同的排列方式。小B的同学现在有一个问题:如果一个整数的质因子的不同的排列方式的种类数为k,那么这个整数n(n>1)最小是多少?小B的同学一共有T个不同的k值,希望小B帮助这个同学解决问题。但是小B发现T太大了,并且给出的k值也相当大,因此小B向你求助。
样例输入
4
1
2
3
105
样例输出
2
6
12
720
数据范围
对于30%的数据,n<=100000
对于全部的数据,n< 263 2 63 ,k< 263 2 63 ,1<=T<=1000

题解

某位大佬说过,LL范围内一个数最多化为由15个不同的质因数的乘积。
所以直接暴力枚举素数的乘积。
首先, n=p1e1p2e2pmem n = p 1 e 1 ∗ p 2 e 2 ∗ … ∗ p m e m ,则 k=(Σmi=1ei)!Πmi=1(ei)! k = ( Σ i = 1 m e i ) ! Π i = 1 m ( e i ) !
所以符合条件的n一定有 e1e2e3...em e 1 ≥ e 2 ≥ e 3 ≥ . . . ≥ e m
通过暴力,求出了≤50000个合法的数。然后通过维护质因数分解的前缀和来求出各自的k,然后再排序。
但是有一种更简单的方法。
突然发现,

(Σmi=1ei)!Πmi=1(ei)!Cem+1Σm+1i=1ei=(Σm+1i=1ei)!Πm+1i=1(ei)! ( Σ i = 1 m e i ) ! Π i = 1 m ( e i ) ! ∗ C Σ i = 1 m + 1 e i e m + 1 = ( Σ i = 1 m + 1 e i ) ! Π i = 1 m + 1 ( e i ) !

所以直接维护组合数就可以了。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define N 110
#define M 45010
#define LL long long
#define P(a) putchar(a)
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
    LL ls,k;
};note md[M];
LL i,j,k,l,r,mid,ans;
LL n,mx,lim,up,x,cnt,w,T,m[N];
LL o[N],tar[N];
LL mi[N][N];
LL ls[M],a[N];
LL p[N],e[N];
LL sum[N][N];
bool pp;
void write(LL x){
    if(x>9)write(x/10);
    P(x%10+'0');
}
void dg(LL x,LL y,LL z){
    LL i,j,l;
    if(x>18)return;
    if(z<=up)ls[ls[0]++]=z;
    l=y<m[x]?y:m[x];
    fd(i,l,1){
        j=up/z;
        if(j<mi[x][i])continue;
        dg(x+1,i,z*mi[x][i]);
    }
}
void gett(){
    LL i,x,k;
    fo(i,2,62){
        x=i;p[0]=0;k=1;
        while(x>1&&k*k<=i){
            k++;
            if(x%k==0){
                p[++p[0]]=k;
                e[p[0]]=0;
                while(x%k==0)x/=k,e[p[0]]++;
            }
        }
        if(x>1)p[++p[0]]=x,e[p[0]]=1;
        fo(j,1,19)sum[i][j]=sum[i-1][j];
        fo(j,1,p[0])sum[i][tar[p[j]]]+=e[j];
    }
}
bool cmp(note x,note y){return x.k<y.k || x.k==y.k &&(x.ls<y.ls);}
int main(){
    up=9223372036854775807;
    o[1]=2;o[2]=3;o[3]=5;o[4]=7;o[5]=11;o[6]=13;o[7]=17;o[8]=19;
    o[9]=23;o[10]=29;o[11]=31;o[12]=37;o[13]=41;o[14]=43;o[15]=47;
    o[16]=53;o[17]=59;o[18]=61;
    fo(i,1,18)tar[o[i]]=i;
    fo(i,1,18){
        lim=floor(log(1.0*up)*1.0/log(1.0*o[i]));
        mx=1;mi[i][0]=1;
        fo(j,1,lim)
            mi[i][j]=mi[i][j-1]*o[i];
        m[i]=lim;
        while(mi[i][m[i]]<0)m[i]--;
    }
    dg(1,62,1);ls[0]--;
    gett();
    fo(i,1,ls[0]){
        x=ls[i];p[0]=0;k=1;
        while(x>1&&k*k<=ls[i]){
            k++;
            if(x%k==0){
                p[++p[0]]=k;
                e[p[0]]=0;
                while(x%k==0)x/=k,e[p[0]]++;
            }
        }
        if(x>1)p[++p[0]]=x,e[p[0]]=1;
        w=0;
        fo(j,1,p[0])w+=e[j];
        fo(j,1,18)a[j]=sum[w][j];
        fo(j,1,p[0])
            fo(k,1,18)a[k]-=sum[e[j]][k];
        k=1;pp=0;
        fo(j,1,19)
            if(a[j]>0){
                l=up/k;
                if(l>=mi[j][a[j]]){
                    k*=mi[j][a[j]];
                }else{
                    pp=1;
                    break;
                }
            }
        if(!pp){
            md[++cnt].ls=ls[i];
            md[cnt].k=k;
        }
    }
    sort(md+1,md+cnt+1,cmp);
    scanf("%lld",&T);
    while(T--){
        scanf("%lld",&x);
        l=ans=1;r=cnt;
        while(l<=r){
            mid=(l+r)>>1;
            if(md[mid].k<x)l=mid+1;
            else ans=mid,r=mid-1;
        }
        printf("%lld\n",md[ans].ls);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值