POJ 1845 - Sumdiv(唯一分解+递推)

题目链接 https://vjudge.net/problem/POJ-1845

【题目描述】
Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^B. Determine S modulo 9901 (the rest of the division of S by 9901).
Input
The only line contains the two natural numbers A and B, (0 <= A,B <= 50000000)separated by blanks.
Output
The only line of the output will contain S modulo 9901.
Sample Input
2 3
Sample Output
15
Hint
2^3 = 8.
The natural divisors of 8 are: 1,2,4,8. Their sum is 15.
15 modulo 9901 is 15 (that should be output).

【题意】
给定a,b,要求a^b的所有因子之和对9901取模后的结果(1 <= a, b <= 5e7)
bb
【思路】
先对a进行唯一分解,a=p1^a1×p2^a2×…×pn^an,那么a^b=p1^(a1×b)×p2^(a2×b)×…×pn^(an×b),所以这个数的所有因子之和sum=(1+p1+p1^2+…+p1^(a1×b))×(1+p2+p2^2+…+p2^(a2×b))×(1+pn+pn^2+…+pn^(an×b)),每个括号里都是枚举选择每种素因子的个数,每个括号里的式子都是一个等比数列,可以递归地求解sum(x,n)=1+x+x^2+…+x^n,当n为奇数时y=(1+x+x^2+…+x^(n/2))+(x^(n/2+1)+x^(n/2+2)+…+x^n),提公因式就能得到
sum(x,n)=(1+x+x^2+…+x^(n/2))×(1+x^(n/2+1))=sum(x,n/2)×(1+x^(n/2+1))
同理当n为偶数时可以同同样的方法推出
sum(x,n)=sum(x,n/2-1)×(1+x^(n/2+1))+x^(n/2),这里的除法’/’指的都是整除。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod = 9901;

ll a, b;
int fac[10050], cnt[10050], num;

void init() {
    num = 0;
    memset(cnt, 0, sizeof(cnt));
    int m = (int)sqrt(a + 0.5);
    ll cpy = a;
    for (int i = 2; i <= m; ++i) {
        if (cpy % i == 0) {
            fac[num] = i;
            while (cpy%i == 0) {
                cpy /= i;
                ++cnt[num];
            }
            ++num;
        }
    }
    if (cpy > 1) {
        fac[num] = cpy;
        ++cnt[num];
        ++num;
    }
}

ll pow_mod(ll x, ll n) {
    ll ans = 1;
    while (n) {
        if (n & 1) ans = (ans * x) % mod;
        x = (x * x) % mod;
        n >>= 1;
    }
    return ans;
}

ll sum(ll x, ll n) {//计算1+x+x^2+...+x^n
    if (0 == n) return 1;
    if (n & 1) {
        return sum(x,n/2) * (1+pow_mod(x,n/2+1)) % mod;
    }
    else {
        return (sum(x, n/2-1) * (1+pow_mod(x,n/2+1)) + pow_mod(x, n / 2)) % mod;
    }
}

int main() {
    while (scanf("%lld%lld", &a, &b) == 2) {
        init();
        ll ans = 1;
        for (int i = 0; i < num; ++i) {
            ans = (ans * sum(fac[i], cnt[i] * b)) % mod;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

这道题容易想到的还是用等比数列求和公式去做,1+x+x^2+…+x^n=(x^(n+1)-1)/(x-1),但是这涉及到了除法,虽然9901是素数,正常情况下可以用逆元去做,但是如果我们要知道一个数a模9901的逆元,而a恰好是9901的整数倍,那么逆元就不存在了,所以不能直接用逆元。但我们在计算一个分式A/B(mod p)的时候,有A/B(mod p)=Amod(B*p)/B,可以通过这个式子来求解,注意快速幂要用大数乘法,这道题的数据会爆long long。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int Mod = 9901;

ll fac[10050], cnt[10050], num;

void init(ll a) {
    num = 0;
    memset(cnt, 0, sizeof(cnt));
    int m = (int)sqrt(a + 0.5);
    ll cpy = a;
    for (int i = 2; i <= m; ++i) {
        if (cpy % i == 0) {
            fac[num] = i;
            while (cpy%i == 0) {
                cpy /= i;
                ++cnt[num];
            }
            ++num;
        }
    }
    if (cpy > 1) {
        fac[num] = cpy;
        ++cnt[num];
        ++num;
    }
}

ll mul_mod(ll a, ll b, ll mod) {//大数乘法取模
    ll ans = 0;
    while (b) {
        if (b & 1) ans = (ans + a) % mod;
        a = (a + a) % mod;
        b >>= 1;
    }
    return ans;
}

ll pow_mod(ll x, ll n, ll mod) {
    ll ans = 1;
    while (n) {
        if (n & 1) ans = mul_mod(ans, x, mod);
        x = mul_mod(x, x, mod);
        n >>= 1;
    }
    return ans;
}

int main() {
    ll a, b;
    while (scanf("%lld%lld", &a, &b) == 2) {
        init(a);
        ll ans = 1;
        for (int i = 0; i < num; ++i) {
            ll M = Mod * (fac[i] - 1);
            ans *= (pow_mod(fac[i], cnt[i] * b + 1, M) - 1 + M) / (fac[i] - 1);
            ans %= Mod;
        }
        printf("%lld\n", ans);
    }
    return 0;
}
posted @ 2018-02-28 16:48  不想吃WA的咸鱼  阅读(90)  评论(0编辑  收藏  举报