数(数学)

数(数学)

给出n(<=300)个数(<=1e18),它们的乘积是x。对于任意\(d_i\mid x\),有\(d_i^{k_i} \mid x,d_i^{k_i+1}\nmid x\),求最大的\(k_i\),并输出当\(k_i\)最大时,有多少个\(d_i\)满足条件。

首先,这道题的思路肯定是对x分解质因数,也就是对n个数分解质因数,然后求出质因数的最多出现次数c,和出现次数最多的质因数个数d,答案分别是\(c\)\(2^d\)

现在问题来了,给出的一个数最多可以到\(10^{18}\),怎么分解质因数呢?先把小于1e6的质数都筛出来,把它们对这n个数试除。除完了以后,n个数中,没有数的因子小于1e6。因此,每个数x最多由两个质数相乘。

到这一步以后,我们就可以通过两两gcd把出现一次以上的质因子确定出来。剩下的数中的质因子,肯定只会在自己那个数中出现。因此,通过\(\sqrt{n}^2\)的方法和Miller-Rabin算法,就可以判断出这个数是由单独一个质因子组成,还是由两个相同质因子组成,抑或是由两个不同质因子组成。对于只出现过一次的质因子,我们并不关心它的值。

#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long LL;

//////Miller-Rabin素数判定//////
const LL m=7, A[m]={2, 3, 5, 7, 11, 13, 17};
LL fmul(LL a, LL b, LL p){  //将b分解为二进制,返回a*b%p
    LL ans=0;
    for (; b; b>>=1, a+=a, a%=p)
        if (b&1) ans+=a, ans%=p;
    return ans;
}
LL fpow(LL a, LL x, LL p){
    LL ans=1, base=a;
    for (; x; x>>=1, base=fmul(base, base, p))
        if (x&1) ans=fmul(ans, base, p);
    return ans;
}
bool MR(LL a, LL x, LL p){  //判断是否a^x=1或p-1 (mod p),且mr下去也成立
    LL t=fpow(a, x, p);
    if (t!=1&&t!=p-1) return false;
    if (t==1&&x&1||t==p-1) return true;
    return MR(a, x>>1, p);
}
bool isprime(LL p){
    if (p&1==0) return false;
    for (LL i=0; i<m; ++i){
        if (p==A[i]) return true;  //互质时费马小定理才成立
        if (fpow(A[i], p-1, p)!=1) return false;
        if (!MR(A[i], (p-1)>>1, p)) return false;
    }
    return true;
}

const LL maxn=305, maxs=1e6+5;
LL n, a[maxn], tmp[maxn], tmpn, c[maxn];

LL p[maxs], cntp, factor[maxs];
void sieve(LL n){
    for (LL i=2; i<n; ++i){
        if (factor[i]) continue;
        p[cntp++]=i;
        for (LL j=i; j<n; j+=i) factor[j]=i;
    }
}

LL allp[maxn*60], tl;
LL b[maxn*maxn], tl2;  //extrap1:没有被列入allp的,出现次数为1的质数还有多少个
LL appear[maxn*60];

LL gcd(LL x, LL y){ return y?gcd(y, x%y):x; }
LL sqr(LL x){ return x*x; }
LL x[maxn*2];

int main(){
    //freopen("number.in", "r", stdin); freopen("number.out", "w", stdout);
    sieve(1e6);
    scanf("%lld", &n);
    for (LL i=0; i<n; ++i){
        scanf("%lld", &a[i]);
        for (LL j=0; j<cntp; ++j)
            while (a[i]%p[j]==0){
                a[i]/=p[j]; allp[tl++]=p[j]; }
    }
    for (int i=0; i<n; ++i) tmp[i]=a[i]; tmpn=n;
    sort(a, a+n);  n=unique(a, a+n)-a;
    for (int i=0; i<n; ++i)  //对a去重,保存重复个数
        for (int j=0; j<tmpn; ++j) if (a[i]==tmp[j]) ++c[i];
    for (LL i=0; i<n; ++i)
        for (LL j=0; j<n; ++j)
            if (a[i]!=a[j]) b[tl2++]=gcd(a[i], a[j]);
    sort(b, b+tl2); tl2=unique(b, b+tl2)-b;  //1和所有大于1e6且在至少两个数上出现的质数 tl2最多到2n
    for (LL i=0; i<n; ++i)  //把所有能分解的数都分解了,剩下的数,素因子一定只有自己有
        for (LL j=0; j<tl2; ++j){
            if (b[j]==1) continue;
            if (a[i]%b[j]==0){
                while (c[i]--) allp[tl++]=b[j],
                    allp[tl++]=a[i]/b[j];
                a[i]=1;
            }
        }
    for (LL i=0; i<n; ++i){
        if (a[i]==1) continue;
        if (sqr(sqrt(a[i]))==a[i]){  //如果A=a*a
            a[i]=sqrt(a[i]);
            while (c[i]--) allp[tl++]=a[i], allp[tl++]=a[i];
            continue;
        }
        if (isprime(a[i])) ++appear[c[i]]; else appear[c[i]]+=2;
    }
    sort(allp, allp+tl); LL j=1, maxm=0;
    for (LL i=1; i<tl; ++i, ++j){  //appear[j]:j次的素数有多少个
        if (allp[i]!=allp[i-1]){
            if (allp[i-1]!=1) ++appear[j];
            j=0;
        }
    }
    if (tl) ++appear[j];
    for (maxm=maxn*60-1; maxm>=0; --maxm) if (appear[maxm]) break;
    printf("%lld\n", maxm); x[0]=1;
    for (LL i=0; i<appear[maxm]; ++i){
        for (LL j=0; j<maxn*2; ++j) x[j]*=2;
        for (LL j=0; j<maxn*2-1; ++j) x[j+1]+=x[j]/10, x[j]%=10;
    } LL len;
    for (len=maxn*2-1; len>=0; --len) if (x[len]) break;
    --x[0];
    for (LL i=0; i<maxn*2; ++i) if (x[i]<0) x[i]+=10, --x[i+1];
    for (LL i=len; i>=0; --i) printf("%lld", x[i]);
    //fclose(stdin); fclose(stdout);
    return 0;
}
posted @ 2018-07-16 18:35  pechpo  阅读(286)  评论(0编辑  收藏  举报