【题解】洛谷6月月赛 —— 「数学」约数个数和

  看德国战墨西哥去了结果发现比赛只剩下30分钟……当然之后又思考这题挺久也还是不会做。看了一下题解,觉得这个做法挺厉害的,在这里记录一下:

  原式实际上就是:(\(K += 1\)) \(\prod C\left ( a_{i} + K, a_{i} \right )\)    

  为什么是这样呢?我们首先注意到,将原数因式分解了之后,每一位质因子取任意 \( <= a_{i}\) 的数,组合起来都是一个合法因数。我们将取出来的任意数看作是在进行选择。那么如果我在其中选择了两个数,我们可以看做是先选取了大的数。这样就可以代表:对于含有\(p_{i}^{a_{i}}\) 因子的约数而言,我们找到了一个它的含有因子\(p_{i}^{a_{i'}}\)的约数。所以上面的式子就可以统计出一个数的约数个数和的约数个数和的……

  然后只需要考虑怎样实现。首先筛掉1e6以内的素数。这样的话,剩下的 \(n\) 可能的情况只有三种:\(p^{2}, p * q, p\) 其中 \(p, q\) 均为质数。这里运用的是Miller_Rabin。要保证正确性还应当加入二次探测~

#include <bits/stdc++.h>
using namespace std;
#define maxx (1e6 + 5)
#define maxn 1000020
#define LL long long
int pri[maxn], tot;
const int P = 998244353;
int m, inv[maxn], cnt[maxn];
LL n, K;
bitset <maxn> is_pri;

void Get_Pri()
{
    for(int i = 2; i <= maxx; i ++)
    {
        if(!is_pri[i]) pri[++ tot] = i;
        for(int j = 1; j <= tot; j ++)
        {
            int t = pri[j] * i;
            if(t > maxx) break;
            is_pri[t] = 1;
            if(!(i % pri[j])) break;
        }
    }
}

LL Mul(LL x, LL times, LL P) {
    x %= P;
    LL base = 0;
    for (; times; times >>= 1, x = (x + x) % P)
        if (times & 1) base = (base + x) % P;
    return base;
}

LL Qpow(LL x, LL times, LL P)
{
    x %= P; LL base = 1;
    for(; times; times >>= 1, x = Mul(x, x, P))
        if(times & 1) times = Mul(times, x, P);
    return base;
}

int C(LL n, LL m)
{
    if(n < m) return 0;
    if(n >= P || m >= P) return 1LL * C(n / P, m / P) * C(n % P, m % P) % P;
    int ans = 1;
    for(int i = 1; i <= m; i ++)
        ans = ans * (n - i + 1LL) % P * inv[i] % P;
    return ans;
}

int main()
{
    Get_Pri(); inv[1] = 1;
    for(int i = 2; i <= 200; i ++)
        inv[i] = 1LL * (P - (P / i)) * inv[P % i] % P; 
    scanf("%lld%lld", &n, &K); K += 1;
    for(int i = 1; i <= tot; i ++)
    {
        int p = pri[i];
        if(!(n % p))
        {
            cnt[++ m] = 0; 
            while(!(n % p)) cnt[m] ++, n /= p;
        }
    }
    if(n > 1)
    {
        LL T = sqrt(n);
        if(T * T == n) cnt[++ m] = 2;
        else
        {
            int tag = 1;
            for(int CNST = 1000; CNST && tag; CNST --)
            {
                int x = rand();
                while(!(x % n)) x = rand();
                tag &= (Qpow(x, n - 1, n) == 1);
            }
            cnt[++ m] = 1;
            if(!tag) cnt[++ m] = 1;
        }
    }
    int ans = 1;
    for(int i = 1; i <= m; i ++) 
        ans = 1ll * ans * C(cnt[i] + K, cnt[i]) % P;
    printf("%d\n", ans);
    return 0;
}

 

posted @ 2018-06-19 21:44  Twilight_Sx  阅读(197)  评论(0编辑  收藏  举报