约数之和

约数之和

题目描述

约数之和

思路分析

对A分解质因数,所以 A = p 1 c 1 ∗ p 2 c 2 ∗ ⋯ p n c n A=p_1^{c_1}*p_2^{c_2}*\cdots p_n^{c_n} A=p1c1​​∗p2c2​​∗⋯pncn​​,那么 A B A^B AB就可以表示为: A B = p 1 B ∗ c 1 ∗ p 2 B ∗ c 2 ∗ ⋯ p 3 B ∗ c n A^B=p_1^{B*c_1}*p_2^{B*c_2}*\cdots p_3^{B*c_n} AB=p1B∗c1​​∗p2B∗c2​​∗⋯p3B∗cn​​,由约数个数定理可知,对于 p 1 B ∗ c 1 p_1^{B*c_1} p1B∗c1​​来说。它的因子有: p 1 0 、 p 1 1 、 ⋯   , p 1 B ∗ c 1 p_1^0、p_1^1、\cdots ,p_1^{B*c_1} p10​、p11​、⋯,p1B∗c1​​,共有 ( B ∗ c 1 + 1 ) (B*c_1+1) (B∗c1​+1)个因子。对于 p n B ∗ c n p_n^{B*c_n} pnB∗cn​​来说,它的因子有: p n 0 、 p n 1 、 ⋯ p n B ∗ c n p_n^0、p_n^1、\cdots p_n^{B*c_n} pn0​、pn1​、⋯pnB∗cn​​,共有 ( B ∗ c n + 1 ) (B*c_n+1) (B∗cn​+1)个因子。根据乘法分配律, A B A^B AB的所有约数之和就是: ( 1 + p 1 1 + p 1 2 + ⋯ + p 1 B ∗ c 1 ) ( 1 + p 2 1 + p 2 2 + ⋯ + p 2 B ∗ c 2 ) ⋯ ( 1 + p n 1 + p n 2 + ⋯ + p n B ∗ c n ) (1+p_1^1+p_1^2+\cdots +p_1^{B*c_1})(1+p_2^1+p_2^2+\cdots +p_2^{B*c_2})\cdots (1+p_n^1+p_n^2+\cdots +p_n^{B*c_n}) (1+p11​+p12​+⋯+p1B∗c1​​)(1+p21​+p22​+⋯+p2B∗c2​​)⋯(1+pn1​+pn2​+⋯+pnB∗cn​​)。该式子中每个括号内都是等比数列,如果使用等比数列求和,即用 S n = a 1 ( 1 − q n ) ( 1 − q ) S_n=\dfrac {a_1(1-q^n)}{(1-q)} Sn​=(1−q)a1​(1−qn)​

那么就需要做除法。而且题目还要求对9901取模,但是mod运算它只对加法、减法、乘法具有分配律,而对除法没有分配律。因此,不能直接分别对分子取模、分母取模后,再做除法取模,即 a b m o d p ≠ a m o d p b m o d p m o d p \dfrac {a}{b}\quad mod \quad p\neq \dfrac {a\quad mod\quad p}{b\quad mod\quad p}mod \quad p ba​modp​=bmodpamodp​modp。因此,我们需要转换一种思路,可以使用分治算法对等比数列进行求和,曲线救国,避免使用除法来取模。

分治算法是把一个问题分解成若干规模更小的同类子问题,对这些子问题进行递归求解,然后在回溯时通过这些子问题的解推导出原问题的最终解。


将问题转换为使用分治算法求: s u m ( p , c ) = 1 + p 1 + p 2 + ⋯ + p c sum(p,c)=1+p^1+p^2+\cdots +p^c sum(p,c)=1+p1+p2+⋯+pc=?

  1. 当c是奇数时, s u m ( p , c ) = ( 1 + p 1 + ⋯ + p c − 1 2 ) + ( p c + 1 2 + ⋯ + p c ) sum(p,c)=(1+p^1+\cdots +p^{\dfrac {c-1}{2}})+(p^{\dfrac {c+1}{2}}+\cdots +p^c) sum(p,c)=(1+p1+⋯+p2c−1​)+(p2c+1​+⋯+pc)= ( 1 + p 1 + ⋯ + p c − 1 2 ) + p c + 1 2 ( 1 + p 1 + ⋯ + p c − 1 2 ) (1+p^1+\cdots +p^{\dfrac {c-1}{2}})+p^{\dfrac {c+1}{2}}(1+p^1+\cdots +p^{\dfrac {c-1}{2}}) (1+p1+⋯+p2c−1​)+p2c+1​(1+p1+⋯+p2c−1​)= ( 1 + p c + 1 2 ) ( 1 + p 1 + ⋯ + p c − 1 2 ) (1+p^{\dfrac {c+1}{2}})(1+p^1+\cdots +p^{\dfrac {c-1}{2}}) (1+p2c+1​)(1+p1+⋯+p2c−1​)= ( 1 + p c + 1 2 ) ∗ s u m ( p , c − 1 2 ) (1+p^{\dfrac {c+1}{2}})*sum(p,\dfrac {c-1}{2}) (1+p2c+1​)∗sum(p,2c−1​)。
  2. 当c是偶数时, s u m ( p , c ) = ( 1 + p 1 + ⋯ + p c − 1 ) + p c sum(p,c)=(1+p^1+\cdots +p^{c-1})+p^c sum(p,c)=(1+p1+⋯+pc−1)+pc,因为c是偶数,所以c-1就是奇数,把c-1带入已经推导出来的奇数式子中可以得到: s u m ( p , c − 1 ) = ( 1 + p c 2 ) ∗ s u m ( p , c 2 − 1 ) sum(p,c-1)=(1+p^{\dfrac {c}{2}})*sum(p,\dfrac {c}{2}-1) sum(p,c−1)=(1+p2c​)∗sum(p,2c​−1)。因此, s u m ( p , c ) = ( 1 + p c 2 ) ∗ s u m ( p , c 2 − 1 ) + p c sum(p,c)=(1+p^{\dfrac {c}{2}})*sum(p,\dfrac {c}{2}-1)+p^c sum(p,c)=(1+p2c​)∗sum(p,2c​−1)+pc。
时间复杂度分析

每次分治之后,问题规模就会缩小一半,然后再运用快速幂算法,就可以再 O ( l o g c ) O(logc) O(logc)的时间内求出等比数列的和。

代码如下:

#include<iostream>
using namespace std;
const int mod=9901;
//利用快速幂算法求出a^b%mod
int qmi(int a,int b)
{
    int ans=1;
    a=a%mod;	//因为有可能a很大,那么执行下面的a=a*a%mod语句时,a*a有可能溢出,所以需要先取模一下。
    while(b)
    {
        if(b&1)
            ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
//求出等比数列之和
int sum(int p,int c)
{
    if(c==0)	//如果c为0,那么p^c=p^0=1,直接返回结果1就好了
		return 1;
    if(c%2)	//如果c是奇数,则带入推导出来的奇数公式
        return ((1+qmi(p,(c+1)/2))*sum(p,(c-1)/2))%mod;
    else	//如果c是偶数,则带入推导出来的偶数公式
        return ((1+qmi(p,c/2))*sum(p,(c/2-1))+qmi(p,c))%mod;
}
int main()
{
    int a,b;
    cin >>a>>b;
    int ans=1;
    //试除法分解质因子
    //for循环先对2到sqrt(n)之内的数进行分解质因子
    for(int i=2;i*i<=a;i++)		//i相当于公式中的p,即质因子
    {
        int s=0;	//记录指数  s相当于公式中的Ci(比如C1、C2...)
        while(a%i==0)
        {
            a/=i;
            s++;
        }
        ans=ans*sum(i,s*b)%mod;		//s*b相当于公式中的c
    }
    //如果退出for循环后,a>1,说明a有且仅有一个大于sqrt(n)的质因子(有可能是a自身)。说明此时的a并不能被sqrt(n)以内的数整除,那么a它的指数就只能是1了。即a^1。
    //所以公式中的c=b*1,即c=b。
    if(a>1)
        ans=ans*sum(a,b)%mod;
    //因为题目说了a,b不能同时为0,但是有可能a为0,因为0的任何方都是0,所以,此时结果应该为0,而不是初始的ans=1
    if(a==0)
        ans=0;
    printf("%d\n",ans);
    return 0;
}

上一篇:P3628 [APIO2010]特别行动队


下一篇:多项式笔记(二)