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

Description

一天,小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向你求助。

Input

第一行,一个整数T。

接下来的T行,每行一个整数k。

Output

T行,每行一个整数,其中第i行的整数表示第i个k值对应的n的值。

Sample Input

4

1

2

3

105

Sample Output

2

6

12

720

Data Constraint

对于30%的数据,1 < n<=100000;

对于全部的数据,1 < n<2^63,1< k<2^63,1<=T<=1000。

题解

如果一个数 X=ap11ap22...apnn
那么它的排列方式就要: (p1+p2+...+pn)!p1!+p2!+...+pn!

因为要求的数最小,所以在同一种方案数中
一定是 p1p2...pn 最优,
而且可以发现真正的质因数很少不到20个,
而且在满足上述条件,且不≥ 263 的数不多。

所以先所有的数预处理出来,存在一个数组里面,排好序,
询问的时候就直接二分出答案。

code

#include<queue>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <time.h>
#include <map>
#define ll long long
#define N 100003
#define M 103
#define db double
#define P putchar
#define G getchar
#define zz 9223372036854775807
#define mo 2147483647
using namespace std;
char ch;
void read(ll &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

int ss[19]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67},tot,l,r,mid;
ll k,T,s[19][80],mx[19],C[80][80];
struct node
{
    ll x,y;
}w[200000];

bool cmp(node a,node b)
{
    return a.y<b.y || (a.y==b.y && a.x<b.x);
}

void dg(int x,int lst,ll S,ll sum,int p)
{
    if(x>18)return;
    tot++; 
    w[tot].x=S;
    w[tot].y=sum;
    //t[S]=sum;
    //t.insert(std::pair<ll,ll>(S,sum));
    //printf("%d : %d %d %lld %lld %d\n",tot,x,lst,S,sum,p);
    lst=min(lst,mx[x]);
    for(int i=lst;i;i--)
        if(S<=zz/s[x][i] && (C[p+i+1][i+1]!=0 && sum<=zz/C[p+i+1][i+1]))
            dg(x+1,i,S*s[x][i],sum*C[p+i+1][i+1],p+i);
}

int main()
{
    for(int i=0;i<19;i++)
        for(s[i][0]=1,mx[i]=0;s[i][mx[i]]<=zz/ss[i];mx[i]++)
            s[i][mx[i]+1]=s[i][mx[i]]*ss[i];

    for(int i=1;i<=80;i++)
    {
        C[i][i]=C[i][1]=1;
        for(int j=2;j<i;j++)
            if(C[i-1][j] && C[i-1][j-1])C[i][j]=C[i-1][j]+C[i-1][j-1];
    } 

    dg(0,mx[0],1,1,0);

    sort(w+1,w+1+tot,cmp);

    freopen("factors.in","r",stdin);
    freopen("factors.out","w",stdout);

    for(read(T);T;T--)
    {
        read(k);
        if(k==1)
        {
            P('2');P('\n');
            continue;
        }
        l=1;r=tot;
        while(l<r)
        {
            mid=(l+r)>>1;
            if(w[mid].y<k)l=mid+1;else r=mid;
        }
        write(w[l].x);
        P('\n');
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值