【POJ 1845】Sumdiv

题目描述

AB A B 的所有约数之和 mod9901 mod 9901 1A,B5×107 1 ≤ A , B ≤ 5 × 10 7

算法分析

A A 分解质因数得 A=p0c0p1c1...Pncn,则 AB=pc0B0pc1B1...PcnBn A B = p 0 c 0 B p 1 c 1 B . . . P n c n B ,它的约数集合就是 {pk00pk11...Pknn|0kiciB} { p 0 k 0 p 1 k 1 . . . P n k n | 0 ≤ k i ≤ c i B }

根据乘法原理, AB A B 的约数之和为 (p00+p10+...+pc0B0)×(p01+p11+...+pc1B1)×...×(p0n+p1n+...+pcnBn) ( p 0 0 + p 0 1 + . . . + p 0 c 0 B ) × ( p 1 0 + p 1 1 + . . . + p 1 c 1 B ) × . . . × ( p n 0 + p n 1 + . . . + p n c n B )

上面乘法运算的每个因数都是一个等比数列,但由于公式涉及除法,而取模运算对除法不满足分配率,因此不能用等比数列求和公式求解。

我们考虑分治,将 x0+x1+...+xn x 0 + x 1 + . . . + x n 拆成 x0+x1+...+xn12+(x0+x1+...+xn12)×xn12+1 x 0 + x 1 + . . . + x ⌊ n − 1 2 ⌋ + ( x 0 + x 1 + . . . + x ⌊ n − 1 2 ⌋ ) × x ⌊ n − 1 2 ⌋ + 1 ,若元素个数是奇数,则还要再乘上 xn x n ,这样就把问题规模缩小了一半,配合快速幂,可以在 O(log2n) O ( l o g 2 n ) 的时间复杂度求解。

实现时要提高分解质因数的效率,枚举到 n n 然后特判比枚举到 n n 要快,还要注意乘法运算可能溢出,使用 64 位整数储存中间结果。

代码实现

#include <cstdio>
#include <vector>
const int mod=9901;
std::vector<int> prime;
std::vector<int> times;
void divide(int num) {
    for(int i=2,end=num;i*i<=end;++i) {
        if(num%i==0) {
            prime.push_back(i);
            int cnt=0;
            while(num%i==0) {
                ++cnt;
                num/=i;
            }
            times.push_back(cnt);
        }
    }
    if(num>1) {
        prime.push_back(num);
        times.push_back(1);
    }
}
int pow(int n,int k) {
    int ans=1;
    while(k) {
        if(k&1) ans=ans*n%mod;
        n=(long long int)n*n%mod;k>>=1;
    }
    return ans;
}
int powsum(int n,int k) {
    if(k==0) return 1;
    int temp=powsum(n,(k-1)/2);
    int ans=(temp+temp*pow(n,(k-1)/2+1)%mod)%mod;
    if(!(k&1)) ans=(ans+pow(n,k))%mod;
    return ans;
}
int main() {
    int a,b;scanf("%d%d",&a,&b);
    divide(a);
    for(int i=0,end=times.size();i<end;++i) times[i]*=b;
    int ans=1;
    for(int i=0,end=prime.size();i<end;++i) ans=ans*powsum(prime[i],times[i])%mod;
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值