题目描述:
对于一个正整数 n 和一个非负整数 d,定义 为 n的所有约数的 d 次方之和。如
现给定 n,d求 的值。由于答案可能很大,输出答案对 取模的结果。
输入描述:
由于n可能很大,所以给出n的质因数分解时式
第一行输入一个正整数w和一个非负整数d
接下来w行,每一行输入两个正整数
保证都是素数且互不相同
对于全部测试点,保证
这里有前缀和的思想:
假设一个数字可以分解质因数为:
设所求的和为ans。那么首先我们先把1加上,这个1可以记作第一个因数(这里是2)的零次幂。
对于因数2,可以选1或2个2相乘,则(这里取模省略,下同)
对于因数3,可以选1个和1搭配,或者选1个和搭配,或选1个和搭配......,也可以选2个3和1搭配,两个3和搭配......
这样下来,ans要加的就是
把这个记为A式。
当再进一个5时,他的选择是
可以发现是5*A式
再进一个5,就是再加
发现没有,对于5的一次或二次的选择(记为),最终使得ans加上了选5新加的乘上选5之前的ans。
所以可以利用前缀和思想,进一个系数就让原来的ans加上这个因数乘ans即可。
但是!!!
不过输入量巨大,也就意味着需要把一个因数量级的乘法优化掉。
没错,相信你已经看出来了,等比数列求和。
要注意题目中d是可以等于0的,这就意味还要用到公比是1的特殊公式。
最后a/b对p取模用逆元就好了
代码附在下方:
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
const ll pp=1000000007;
ll quickmod(ll ans,ll x){
ll a=1;
while(x){
if(x%2){
a=(ans%pp*a%pp)%pp;
}
ans=(ans%pp*ans%pp)%pp;
x/=2;
}
return a;
}
int main(){
int w=0;scanf("%d",&w);
ll d=0;scanf("%lld",&d);
ll ans=1;
for(int i=1;i<=w;i++){
ll p,a;
scanf("%lld %lld",&p,&a);
ll dd=quickmod(p,d);
ll up=dd*quickmod(dd,a)-dd;
if(d==0){
up=(dd%pp*a%pp)%pp;
}
ll down=quickmod(dd-1,pp-2);
ll temp=(up%pp*down%pp)%pp;
if(d==0){
ans=((ans%pp)+(ans%pp)*(a%pp))%pp;
}else{
ans=((ans%pp)+(ans%pp)*(temp%pp)%pp)%pp;
}
}
printf("%lld",ans);
return 0;
}
谢谢!