二解 约数和luogu2715

来源:互联网 发布:linux 解压war 编辑:程序博客网 时间:2024/05/18 02:40

问题描述:给出a和b求a^b的约数和。
输入输出格式
输入格式:一行两个数a,b。
输出格式:一个数表示结果对 9901 的模。
输入输出样例
输入样例#1:2 3
输出样例#1:15
说明:对于 30%的数据,a,b≤ 10 对于 100%的数据,0 ≤ a,b ≤ 50 000 000
分析:此题题面简洁,容易想到,要求约数和,则可以利用约数和公式来暴力解,但是数据规模却明显的告诉你会TLE.那么会超时的地方在哪呢,分析下此题的做题步骤:
方法一:递归求等比数列之和
1.将a分解质因数,a=(p1^k1)(p2^k2)(p3^k3)….(pn^kn)
2.将每个质因数的指数 * b ,a^b =(p1^(k1 * b))(p2^(k2 * b))(p3^(k3 * b))….(pn^(kn * b))
3.根据约数和公式:对于已经分解的整数
有a^b的所有因子之和为
S = (1+p1+p1^2+p1^3+…p1^(k1* b)) * (1+p2+p2^2+p2^3+….p2^(k2 * b)) * (1+p3+ p3^2+…+ p3^(k3 * b)) * …. * (1+pn+pn^2+pn^3+…pn^(kn * b))
从上面步骤上来看,可知如果TLE,那一定是在第3步求因子之和上,亲测如果用快速幂并累加和累乘后在luogu上能40分,那么我们仔细观察因子之和的每个乘积因子都是一个等比数列之和,可以利用等比数列公式去计算,但是除法取余需要涉及到逆元【见方法二】,可以利用下图所示的公式递归得出:(此图感谢洛谷用户-恶魔)
这里写图片描述
证明如下图:
这里写图片描述
写到这,说明上面我虽列出3个操作步骤,实际上可以边质因数分解,边把2,3步逐步完成。
代码附上:

#include<iostream>#include<cmath>#define MOD 9901 using namespace std;int a,b,sum;int quickMod(int x,int y){//快速幂    int ans=1;    x %= MOD;    while(y){        if(y&1) ans = (ans * x) % MOD;        y = y/2;        x = (x * x)%MOD;    }    return ans;} int calc(int p,int c)  //利用奇偶性递归求等比数列求和{    if(c == 0) return 1;     if(c&1) return ((quickMod(p,(c+1)/2) + 1)%MOD * calc(p,(c-1)/2) % MOD)% MOD;    else return (((quickMod(p,c/2) + 1) %MOD * calc(p,c/2 - 1)%MOD )%MOD + quickMod(p,c))%MOD;} int solve(){    int ans = 1,cnt;    //质因数分解    for(int i = 2,t = sqrt(a); i<= t; i++)    {        if(a%i==0)        {            cnt = 0;  //统计i的指数             while(a%i==0){                ++cnt;                a = a/i;            }               int q = cnt * b;            sum = calc(i,q);               ans = (ans * sum)%MOD;        }     }     if(a >1){            cnt = 1;            int q = cnt * b;            sum = calc(a,q);            ans = (ans * sum)%MOD;     }     return ans; } int main(){    scanf("%d%d",&a,&b);        printf("%d",solve());    return 0; } 

方法二:逆元求等比数列求和,其他不变

#include<iostream>#include<cstdio>#include<cmath>#define MOD 9901 using namespace std;int a,b,sum;const int MD = 9901;typedef long long LL;LL exgcd(LL a,LL b,LL &x,LL &y)  //扩展欧几里得{    if(b==0){        x = 1;y =0; return a;    }    else{        LL d = exgcd(b,a%b,x,y);        LL t = x; x = y; y = t - a/b * y;               return d;    }}LL inv(LL b,LL n){  //求逆元    LL x,y;    LL d = exgcd(b,n,x,y);    if(d==1) return (x%n + n)%n;    else return -1;}int quickMod(int x,int y){//快速幂    int ans=1;    x %= MOD;    while(y){        if(y&1) ans = (ans * x) % MOD;        y = y/2;        x = (x * x)%MOD;    }    return ans;} int solve(){    int ans = 1,cnt;   //质因数分解    for(int i = 2,t = sqrt(a); i<= t; i++)    {        if(a%i==0)        {            cnt = 0;  //统计i的指数             while(a%i==0){                ++cnt;                a = a/i;            }               int q = cnt * b;            int q_1 = inv(i-1,MOD) ;   //求i-1的逆元            sum=((quickMod(i,q+1) - 1) * q_1)%MOD;  //按等比数列求和公式计算和值            ans = (ans * sum)%MOD;        }     }     if(a >1){            cnt = 1;            int q = cnt * b;            LL q_1 = inv(a-1,MOD) ;   //求a-1的逆元            sum=((quickMod(a,q+1) - 1) * q_1)%MOD; //按等比数列求和公式计算和值            ans = (ans * sum)%MOD;     }     return ans; } int main(){    scanf("%d%d",&a,&b);        printf("%d",solve());    return 0; }