【Luogu】P3927 SAC E#1 - 一道中档题 Factorial

【题目】洛谷10月月赛R1 提高组

【题意】求n!在k进制下末尾0的个数,n<=1e18,k<=1e16。

【题解】考虑10进制末尾0要考虑2和5,推广到k进制则将k分解质因数。

每个质因数在n!中的数量,以2为例是n/2+n/4+n/8...这样统计。(含x个就被统计x次)

最后得到凑出的k的个数就可以得到末尾0的个数。

分解质因数复杂度O(√k),也使用pollard rho算法可以加速。

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <stdio.h>

const int Times = 10;
const int N = 5500;

using namespace std;
typedef long long LL;

LL ct, cnt;
LL fac[N], num[N];

LL gcd(LL a, LL b)
{
    return b? gcd(b, a % b) : a;
}

LL multi(LL a, LL b, LL m)
{
    LL ans = 0;
    a %= m;
    while(b)
    {
        if(b & 1)
        {
            ans = (ans + a) % m;
            b--;
        }
        b >>= 1;
        a = (a + a) % m;
    }
    return ans;
}

LL quick_mod(LL a, LL b, LL m)
{
    LL ans = 1;
    a %= m;
    while(b)
    {
        if(b & 1)
        {
            ans = multi(ans, a, m);
            b--;
        }
        b >>= 1;
        a = multi(a, a, m);
    }
    return ans;
}

bool Miller_Rabin(LL n)
{
    if(n == 2) return true;
    if(n < 2 || !(n & 1)) return false;
    LL m = n - 1;
    int k = 0;
    while((m & 1) == 0)
    {
        k++;
        m >>= 1;
    }
    for(int i=0; i<Times; i++)
    {
        LL a = rand() % (n - 1) + 1;
        LL x = quick_mod(a, m, n);
        LL y = 0;
        for(int j=0; j<k; j++)
        {
            y = multi(x, x, n);
            if(y == 1 && x != 1 && x != n - 1) return false;
            x = y;
        }
        if(y != 1) return false;
    }
    return true;
}

LL pollard_rho(LL n, LL c)
{
    LL i = 1, k = 2;
    LL x = rand() % (n - 1) + 1;
    LL y = x;
    while(true)
    {
        i++;
        x = (multi(x, x, n) + c) % n;
        LL d = gcd((y - x + n) % n, n);
        if(1 < d && d < n) return d;
        if(y == x) return n;
        if(i == k)
        {
            y = x;
            k <<= 1;
        }
    }
}

void find(LL n, int c)
{
    if(n == 1) return;
    if(Miller_Rabin(n))
    {
        fac[ct++] = n;
        return ;
    }
    LL p = n;
    LL k = c;
    while(p >= n) p = pollard_rho(p, c--);
    find(p, k);
    find(n / p, k);
}

int main()
{
    LL n,kind;
    scanf("%lld%lld",&kind,&n);
    ct = 0;
    find(n, 120);
    sort(fac, fac + ct);
    num[0] = 1;
    int k = 1;
    for(int i=1; i<ct; i++)
    {
        if(fac[i] == fac[i-1])
            ++num[k-1];
        else
        {
            num[k] = 1;
            fac[k++] = fac[i];
        }
    }
    cnt = k;
    LL ans=(1ll<<60);
    for(int i=0;i<cnt;i++){
        LL as=0,N=kind/fac[i];
        while(N){
            as+=N;
            N/=fac[i];
        }
        as/=num[i];
        ans=min(as,ans);
    }
    if(ans==1ll<<60)ans=0;
    printf("%lld",ans);
    return 0;
}
View Code

 

posted @ 2017-10-08 17:57  ONION_CYC  阅读(221)  评论(0编辑  收藏  举报