POJ 1845 逆元 / 分治

题意

传送门 POJ 1845

题解

分解质因数 A = p 1 e 1 p 2 e 2 … p n e n A=p_1^{e_1}p_2^{e_2}\dots p_n^{e_n} A=p1e1p2e2pnen,则约数和为
( 1 + p 1 + ⋯ + p 1 e 1 ) ( 1 + p 2 + ⋯ + p 2 e 2 ) … ( 1 + p n + ⋯ + p n e n ) (1+p_1+\dots +p_1^{e_1})(1+p_2+\dots +p_2^{e_2})\dots (1+p_n+\dots +p_n^{e_n}) (1+p1++p1e1)(1+p2++p2e2)(1+pn++pnen)

逆元

对于质因数 p i p_i pi,根据等比数列求和公式,括号内求和为 p i e i B + 1 − 1 p i − 1 \frac{p_i^{e_iB+1}-1}{p_i-1} pi1pieiB+11 分子项快速幂求解,分母项逆元求解。对于 p i   m o d   m = 1 p_i\ mod\ m =1 pi mod m=1 的情况需要特殊处理,因为此时 p i − 1 p_i-1 pi1 不存在逆元。对于正整数 N N N,至多有一个大于等于 N \sqrt N N 的质因子,那么逆元打表处理 [ 1 , N ] [1,\sqrt N] [1,N ] 的部分,对于超出这个范围的部分,利用费马小定理进行快速幂求逆元。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;
#define mod 9901
typedef map<int, int> mp;
int A, B, inv[mod];

mp prime_factor(int n)
{
    mp res;
    for (int i = 2; i * i <= n; ++i)
    {
        while (n % i == 0)
        {
            ++res[i];
            n /= i;
        }
    }
    if (n != 1)
        res[n] = 1;
    return res;
}

int mod_pow(int x, int n)
{
    int res = 1;
    x %= mod;
    while (n)
    {
        if (n & 1)
            res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

int get_inv(int n)
{
    if (n < mod)
        return inv[n];
    return mod_pow(n, mod - 2);
}

int main()
{
    inv[1] = 1;
    for (int i = 2; i < mod; ++i)
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    scanf("%d%d", &A, &B);
    mp fac = prime_factor(A);
    int res = 1;
    for (mp::iterator it = fac.begin(); it != fac.end(); ++it)
    {
        int p = it->first, e = it->second * B;
        if (p % mod != 1)
            res = res * (mod_pow(p, e + 1) + mod - 1) % mod * get_inv(p - 1) % mod;
        else
            res = res * (e % mod + 1) % mod;
    }
    printf("%d\n", res);
    return 0;
}
分治

考虑质因数 p i p_i pi,若 e i e_i ei 为奇数,设其括号内求和为 s u m ( p , e ) sum(p,e) sum(p,e),则有
s u m ( p , e ) = ( 1 + p + ⋯ + p ⌊ e / 2 ⌋ ) + ( p ⌊ e / 2 ⌋ + 1 + ⋯ + p e ) = ( 1 + p ⌊ e / 2 ⌋ + 1 ) × s u m ( p , ⌊ e / 2 ⌋ ) sum(p,e)=(1+p+\dots+p^{\lfloor e/2\rfloor})+(p^{\lfloor e/2\rfloor+1}+\dots +p^e)=(1+p^{\lfloor e/2\rfloor+1})\times sum(p,\lfloor e/2\rfloor) sum(p,e)=(1+p++pe/2)+(pe/2+1++pe)=(1+pe/2+1)×sum(p,e/2) 指数为偶数,将 p i e i p_i^{e_i} piei 加入答案后将 e i e_i ei 减一处理为奇数情况即可。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;
#define mod 9901
typedef map<int, int> mp;
int A, B;

mp prime_factor(int n)
{
    mp res;
    for (int i = 2; i * i <= n; ++i)
    {
        while (n % i == 0)
        {
            ++res[i];
            n /= i;
        }
    }
    if (n != 1)
        res[n] = 1;
    return res;
}

int mod_pow(int x, int n)
{
    int res = 1;
    x %= mod;
    while (n)
    {
        if (n & 1)
            res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

int sum(int p, int e)
{
    if (!e)
        return 1;
    int res = 0;
    if (!(e & 1))
        res += mod_pow(p, e), --e;
    e /= 2, res += (1 + mod_pow(p, e + 1)) * sum(p, e);
    return res % mod;
}

int main()
{
    scanf("%d%d", &A, &B);
    mp fac = prime_factor(A);
    int res = 1;
    for (mp::iterator it = fac.begin(); it != fac.end(); ++it)
    {
        int p = it->first, e = it->second * B;
        res = res * sum(p, e) % mod;
    }
    printf("%d\n", res);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值