poj1845 Sumdiv 题解报告

题目传送门

【题目大意】

求$A^B$的所有约数之和($mod 9901$)

【思路分析】

 把$A$分解质因数,则$$A=p_1^{c_1}*p_2^{c_2}*…*p_n^{c_n}$$

于是可以得到

$$A^B=p_1^{B*c_1}*p_2^{B*c_2}*…*p_n^{B*c_n}$$

于是$A^B$的所有约数之和就是

$$(1+p_1+…+p_1^{B*c_1})*…*(1+p_n+…+p_n^{B*c_n})$$

上式的每个括号中都是等比数列,如果用求和公式,需要做除法,而$mod$运算的分配律并不适用于除法,所以我们要考虑转化。

1.递归(分治法)

把问题分成规模更小的同类子问题,然后递归求解

设$sum(p,c)=1+p+p^2+…+p^c$

若$c$为奇数,则

$$sum(p,c)=(1+p+…+p^{frac{c-1}{2}})+(p^{frac{c+1}{2}}+…+p^c)$$

$$=(1+p+…+p^{frac{c-1}{2}})+p^{frac{c+1}{2}}*(1+p+…+p^{frac{c-1}{2}})$$

$$=(1+p^{frac{c+1}{2}})*sum(p,frac{c-1}{2})$$

若$c$为偶数,则

$$sum(p,c)=(1+p^{frac{c}{2}})*sum(p,frac{c}{2}-1)+p^c$$

2.逆元法

关于乘法逆元的知识详见数论专题学习笔记,这里只放一下代码了。

【代码实现】

 1 #include<cstdio>
 2 #define rg register
 3 #define go(i,a,b) for(rg int i=a;i<=b;i++)
 4 #define ll long long
 5 using namespace std;
 6 int a,b,m,ans=1;
 7 const int mod=9901;
 8 int p[20],c[20];
 9 void divide(int x){//分解质因数
10     m=0;
11     for(rg int i=2;i*i<=x;i++){
12         if(x%i==0){
13             p[++m]=i,c[m]=0;
14             while(x%i==0) x/=i,c[m]++;
15         }
16     }
17     if(x>1) p[++m]=x,c[m]=1;
18     return;
19 }
20 int ksm(int x,ll y){
21     int as=1;
22     while(y){
23         if(y&1) as=(ll)as*x%mod;
24         x=(ll)x*x%mod;
25         y>>=1;
26     }
27     return as;
28 }
29 int main(){
30     scanf("%d%d",&a,&b);
31     divide(a);
32     go(i,1,m){
33         if((p[i]-1)%mod==0){//特判没有逆元的情况
34             ans=((ll)b*c[i]+1)%mod*ans%mod;
35             continue;
36         }
37         int x=ksm(p[i],(ll)b*c[i]+1);//分子
38         x=(x-1+mod)%mod;
39         int y=p[i]-1;//分母
40         y=ksm(y,mod-2);//费马小定理求逆元
41         ans=(ll)ans*x%mod*y%mod;
42     }
43     printf("%d
",ans);
44     return 0;
45 }
逆元法
 1 #include<cstdio>
 2 #define rg register
 3 #define go(i,a,b) for(rg int i=a;i<=b;i++)
 4 #define ll long long
 5 using namespace std;
 6 int a,b,m,ans=1;
 7 const int mod=9901;
 8 int p[20],c[20];
 9 void divide(int x){//分解质因数
10     m=0;
11     for(rg int i=2;i*i<=x;i++){
12         if(x%i==0){
13             p[++m]=i,c[m]=0;
14             while(x%i==0) x/=i,c[m]++;
15         }
16     }
17     if(x>1) p[++m]=x,c[m]=1;
18     return;
19 }
20 int ksm(int x,ll y){
21     int as=1;
22     while(y){
23         if(y&1) as=(ll)as*x%mod;
24         x=(ll)x*x%mod;
25         y>>=1;
26     }
27     return as;
28 }
29 int sum(int x,int y){
30     if(y==0) return 1;
31     if(y==1) return x+1;
32     if(y&1)    return (1+ksm(x,(y+1)/2))*sum(x,(y-1)/2)%mod;
33     else return (1+ksm(x,y/2))*sum(x,y/2-1)%mod+ksm(x,y)%mod;
34 }
35 int main(){
36     scanf("%d%d",&a,&b);
37     if(a==0) {printf("0
");return 0;}//注意特判
38     if(b==0) {printf("1
");return 0;}
39     divide(a);
40     go(i,1,m)
41         ans=ans*sum(p[i],b*c[i])%mod;
42     printf("%d
",ans);
43     return 0;
44 }
递归法
原文地址:https://www.cnblogs.com/THWZF/p/11248143.html