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 AB的所有约数之和mod9901( 1 ≤ A , B ≤ 5 ∗ 1 0 7 1\leq A,B\leq5*10^7 1≤A,B≤5∗107)
首先,看到题面直觉告诉就告诉我一定要对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 = p 1 B ∗ c 1 ∗ p 2 B ∗ c 2 ∗ ⋯ ∗ p n B ∗ c n A^B={p_1}^{B*c_1}*{p_2}^{B*c_2}*\cdots*{p_n}^{B*c_n} AB=p1B∗c1∗p2B∗c2∗⋯∗pnB∗cn
现在我们要求出所有的约数之和,约数怎么来呢?
我可以随便抓几个(0到 B ∗ c 1 B*c_1 B∗c1之间) p 1 p_1 p1,随便抓几个(0到 B ∗ c 2 B*c_2 B∗c2之间) p 2 p_2 p2, ⋯ \cdots ⋯,随便抓几个(0到 B ∗ c n B*c_n B∗cn之间) p n p_n pn,然后把它们全部乘起来就可以得到一个 A B A^B AB的约数
我们就可以得到它的和是:
( 1 + p 1 + p 1 2 + ⋯ + p 1 B ∗ c 1 ) ∗ ( 1 + p 2 + p 2 2 + ⋯ + p 2 B ∗ c 2 ) ∗ ⋯ ∗ ( 1 + p n + p n 2 + ⋯ + p n B ∗ c n ) (1+p_1+{p_1}^2+\cdots+{p_1}^{B*c_1})*(1+p_2+{p_2}^2+\cdots+{p_2}^{B*c_2})*\cdots*(1+p_n+{p_n}^2+\cdots+{p_n}^{B*c_n}) (1+p1+p12+⋯+p1B∗c1)∗(1+p2+p22+⋯+p2B∗c2)∗⋯∗(1+pn+pn2+⋯+pnB∗cn)
简单解释一下上面那个式子是怎么得到的:现在我们来脑补一下把上面的括号全部打开,那么每一项都是一个形如这样的式子:
p 1 x 1 ∗ p 2 x 2 ∗ ⋯ ∗ p n x n {p_1}^{x_1}*{p_2}^{x_2}*\cdots*{p_n}^{x_n} p1x1∗p2x2∗⋯∗pnxn
有没有发现就是对应了每一个 A B A^B AB的约数
也就是说如果我们可以快速地求出 1 + p + p 2 + ⋯ + p c 1+p+{p}^2+\cdots+{p}^c 1+p+p2+⋯+pc问题就可以轻松解决了(也就是等比数列求和)
接下来我们讲两个方法算出上面那个东西(一个需要逆元,一个不需要逆元,戳我学习逆元)
我们令 s u m ( p , c ) = 1 + p + p 2 + ⋯ + p c sum(p,c)=1+p+p^2+\cdots+p^c sum(p,c)=1+p+p2+⋯+pc就是我们要求的东西
先来讲一讲不需要逆元的:
我们使用分治
如果c是奇数,那么:
s u m ( p , c ) = ( 1 + p + p 2 + ⋯ + p c − 1 2 ) + ( p c + 1 2 + p c + 3 2 + ⋯ + p c ) sum(p,c)=(1+p+p^2+\cdots+p^\frac{c-1}{2})+(p^\frac{c+1}{2}+p^\frac{c+3}{2}+\cdots+p^c) sum(p,c)=(1+p+p2+⋯+p2c−1)+(p2c+1+p2c+3+⋯+pc)
s u m ( p , c ) = ( 1 + p + p 2 + ⋯ + p c − 1 2 ) + p c + 1 2 ∗ ( 1 + p + p 2 + ⋯ + p c − 1 2 ) sum(p,c)=(1+p+p^2+\cdots+p^\frac{c-1}{2})+p^\frac{c+1}{2}*(1+p+p^2+\cdots+p^{\frac{c-1}{2}}) sum(p,c)=(1+p+p2+⋯+p2c−1)+p2c+1∗(1+p+p2+⋯+p2c−1)
然后你有没有惊奇的发现规模所缩小了一半:
s u m ( p , c ) = ( 1 + p c + 1 2 ) ∗ s u m ( p , c − 1 2 ) sum(p,c)=(1+p^\frac{c+1}{2})*sum(p,\frac{c-1}{2}) sum(p,c)=(1+p2c+1)∗sum(p,2c−1)
美滋滋,我们再来看看如果c是偶数怎么办?好办呀,拎出一个 p c p^c pc就好了呀!然后c-1就是奇数了!,我们把c-1带回上面的公式得到了:
s u m ( p , c ) = ( 1 + p c 2 ) ∗ s u m ( p , c 2 − 1 ) + p c sum(p,c)=(1+p^\frac{c}{2})*sum(p,\frac{c}{2}-1)+p^c sum(p,c)=(1+p2c)∗sum(p,2c−1)+pc
于是偶数的规模也缩小了一半,我们成功地在O(log c)的时间里求出了等比数列的和(当然要用快速幂)
#include
#include
#define ll long long
#define mod 9901
using namespace std;
ll a,b,ans=1;
int p[25],c[25],cnt=0;
void get_pr(int n){
for (int i=2;i*i<=n;i++){
if (n%i==0){
p[++cnt]=i;
while(n%i==0) c[cnt]++,n/=i;
}
}
if (n>1){
p[++cnt]=n;
c[cnt]=1;
}
}
ll pow(ll a,ll b,ll p){
ll ans=1;
while(b){
if (b%2==1) ans=ans*a%p;
a=a*a%p;
b/=2;
}
return ans;
}
ll sum(ll p,ll s){
if (s==0) return 1;
if (s%2==1) return (1+pow(p,(s+1)/2,mod))*sum(p,(s-1)/2)%mod;
else return ((1+pow(p,s/2,mod))*sum(p,s/2-1)%mod+pow(p,s,mod))%mod;
}
int main(){
scanf("%lld%lld",&a,&b);
get_pr(a);
for (int i=1;i<=cnt;i++){
ans=(ans*sum(p[i],1ll*c[i]*b))%mod;
}
cout<<ans;
return 0;
}
我们再来看一下第二种要用逆元的方法(戳我学习逆元)
我们这样来操作一下等比数列:
首先原等比数列长这样:
s u m ( p , c ) = 1 + p + p 2 + ⋯ + p c ⋯ ① sum(p,c)=1+p+p^2+\cdots+p^c\ \cdots① sum(p,c)=1+p+p2+⋯+pc ⋯①
我们给它乘上一个p得到了:
s u m ( p , c ) ∗ p = p + p 2 + p 3 + ⋯ + p c + 1 ⋯ ② sum(p,c)*p=p+p^2+p^3+\cdots+p^{c+1}\ \cdots② sum(p,c)∗p=p+p2+p3+⋯+pc+1 ⋯②
然后我们把②式①式错位相减,你会发现等式右边中间的部分都被抵消没了:
s u m ( p , c ) ∗ ( p − 1 ) = p c − 1 − 1 sum(p,c)*(p-1)=p^{c-1}-1 sum(p,c)∗(p−1)=pc−1−1
我们得到了:
s u m ( p , c ) = p c − 1 − 1 p − 1 sum(p,c)=\frac {p^{c-1}-1}{p-1} sum(p,c)=p−1pc−1−1
模数9901是一个质数,So使用快速幂和逆元我们可以轻松算出这个式子!
BUT,还有一个问题我们没有解决,就是如果(p-1)是9901的倍数那么就没有逆元了,肿么半捏?
现在考虑(p-1)是9901的倍数,我们有:
p m o d 9901 = 1 p\ mod\ 9901=1 p mod 9901=1
那么 1 + p + p 2 + ⋯ + p c 1+p+p^2+\cdots+p^c 1+p+p2+⋯+pc中的每一项模9901得到的答案都是1,So,这个时候 s u m ( p , c ) = c + 1 sum(p,c)=c+1 sum(p,c)=c+1
#include
#include
#define ll long long
#define mod 9901
using namespace std;
ll a,b,ans=1;
int p[25],c[25],cnt=0;
void get_pr(int n){
for (int i=2;i*i<=n;i++){
if (n%i==0){
p[++cnt]=i;
while(n%i==0) c[cnt]++,n/=i;
}
}
if (n>1){
p[++cnt]=n;
c[cnt]=1;
}
}
ll pow(ll a,ll b,ll p){
ll ans=1;
while(b){
if (b%2==1) ans=ans*a%p;
a=a*a%p;
b/=2;
}
return ans;
}
int main(){
scanf("%lld%lld",&a,&b);
get_pr(a);
for (int i=1;i<=cnt;i++){
if ((p[i]-1)%mod==0){
ans=1ll*(b*c[i]+1)%mod*ans%mod;
}
else{
ll x=pow(p[i],1ll*b*c[i]+1,mod);
x=(x-1+mod)%mod;
ll y=pow(p[i]-1,mod-2,mod);
ans=1ll*x*y%mod*ans%mod;
}
}
cout<<ans;
return 0;
}
OK,完事
参考:
《算法竞赛进阶指南》 李煜东 著
于HG机房