问题描述:给出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=(p1k1)(p2k2)(p3k3)….(pn^kn)
2.将每个质因数的指数 * b ,a^b =(p1^(k1 * b))(p2^(k2 * b))(p3^(k3 * b))….(pn^(kn * b))
3.根据约数和公式:对于已经分解的整数
有a^b的所有因子之和为
S = (1+p1+p12+p13+…p1^(k1* b)) * (1+p2+p22+p23+….p2^(k2 * b)) * (1+p3+ p3^2+…+ p3^(k3 * b)) * …. * (1+pn+pn2+pn3+…pn^(kn * b))
从上面步骤上来看,可知如果TLE,那一定是在第3步求因子之和上,亲测如果用快速幂并累加和累乘后在luogu上能40分,那么我们仔细观察因子之和的每个乘积因子都是一个等比数列之和,可以利用等比数列公式去计算,但是除法取余需要涉及到逆元【见方法二】,可以利用下图所示的公式递归得出:(此图感谢洛谷用户-恶魔)
证明如下图:
写到这,说明上面我虽列出3个操作步骤,实际上可以边质因数分解,边把2,3步逐步完成。
代码附上:
#include
#include
#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
#include
#include
#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;
}