POJ1845 Sumdiv - 乘法逆元+快速幂A^B的约数个数和
Posted dprswdr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ1845 Sumdiv - 乘法逆元+快速幂A^B的约数个数和相关的知识,希望对你有一定的参考价值。
POJ1845 Sumdiv
Sol:
约数个数和\(sumdiv=(1+p_1+p_1^2+\dots + p_1^{c_1})*\dots *(1+p_k+p_k^2+\dots + p_k^{c_k})\)
其中每一项都是一个首项为1,公比为\(p_i\)的等比数列的和,即
\(1*\frac{1-p_i^{c_{k}+1}}{1-p_i}=\frac{p_i^{c_{k}+1}-1}{p_i-1}\)
可以通过快速幂+逆元求解。
然而,当\(9901|(p_i-1)\)时,\(p_i-1 ~mod ~9901\)没有乘法逆元。但\(p_i ~mod~9901 \equiv 1 (mod ~9901)\),因此等比数列的和\(\equiv 1+1^2+\dots+1^{c_{k+1}}\equiv c_k+1\)
本题数据范围较大,乘法需要用64位整数乘法实现。
Tips:
1.若\(P|x\),则\(x ~mod~ P\)没有逆元(因为P,x不互质),但\(x+1 \equiv1(mod ~P)\)。
2.数据范围较大时(long long 之间相乘),需要用到64位整数乘法。
AC CODE:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int P = 9901;
typedef long long ll;
int A,B;
int read(){
int x=0,f=1;char ch=‘ ‘;
while(ch>‘9‘||ch<‘0‘) {if(ch==‘-‘) f=-1;ch=getchar();}
while(ch>=‘0‘&&ch<=‘9‘){x=(x<<3)+(x<<1)+(ch^‘0‘);ch=getchar();}
return x*f;
}
ll fmul(ll a,ll b){
ll ans=0;
for(;b;b>>=1){
if(b&1) ans=(ans+a)%P;
a=(a*2)%P;
}
return ans;
}
ll fpow(ll a,ll b){
ll ans=1;
for(;b;b>>=1){
if(b&1) ans=(ans*a)%P;
a=(a*a)%P;
}
return ans;
}
int p[20],c[20];
void get_div(int x){
int tp=x;
int lim=sqrt(x)+1;
for(int i=2;i<=lim;i++){
if(tp%i==0) p[++p[0]]=i;
while(tp%i==0){
tp/=i;
c[p[0]]++;
}
}
if(tp>1){
p[++p[0]]=tp;c[p[0]]=1;
}
}
ll cal(int x){
if((p[x]-1)%P==0){// if p[x]-1 % 9901 = 0 -> there is no inv
return (fmul(B,c[x])%P+1)%P;
}
ll ans=fmul((fpow(p[x],c[x]*B+1)-1+P)%P,fpow(p[x]-1,P-2))%P;
return ans;
}
int main(){
freopen("data.in","r",stdin);
freopen("sol.out","w",stdout);
A=read();B=read();
if(!A) {
printf("0");return 0;
}
else if(A==1){
printf("1");return 0;
}
else if(B==0){
printf("1");return 0;
}
get_div(A);
ll ans=1;
for(int i=1;i<=p[0];i++){
ans=fmul(cal(i),ans)%P;
}
printf("%lld",ans);
return 0;
}
以上是关于POJ1845 Sumdiv - 乘法逆元+快速幂A^B的约数个数和的主要内容,如果未能解决你的问题,请参考以下文章