点击打开链接
今天HH在学数论,他看到一个很优美的式子:
∑ni=1ik mod p
裂解:
这道理上来看一下数据范围,肯定是找规律。。。渣渣数学太渣,证明不出来,猜到了n-p大概100左右,写了三个p之前和为0,p p-1的后面快速幂算的交了三发
当然是wa了,
后来看题解,果然是这样,好接近啊,,,,
官方题解——————————————————————————————————————————————————————————————
看到n比p大了不到100个,我们可以思考一下假如我们只算到p或者p−1会是怎么样的
即考虑函数∑p−1i=1ik mod p
我们先打个表能发现当k是p−1的倍数的时候这个值是p−1,别的时候都是0
然而这是否正确的呢? 我们可以简单的推导一下:
假设g是p的原根,那么根据原根的性质,g的1次方到p−1次方在模p的时候
对应了1到p−1的某个全排列,即原式等价于
gk+g2k+g3k+...+g(p−1)k
然后我们用等比数列求和一下则得到
(gk−gpk)/(1−gk)=(gk−gpk)×inv(1−gk)
再根据费马小定理我们便得到了gp=g,那么这个式子就变成了0
不过这是建立在这是等比数列的情况下的,所以我们对几种不是等比数列的情况需要特判:
(1) p为2的时候原根是1
(2) k为p-1的时候由费马小定理可以得到所有项均为1
所以我们现在只需要算剩下的100个左右的数就可以了,快速幂搞一下就行.
不过要小心这个范围乘法会爆long long 所以需要快速乘/O(1)快速乘
简单来说 a^k%p 有个循环节,循环节长度为 p-1, 当且仅当j==p-1时值为p-1 其他都是0;
最后算一下不足循环的部分。
#include
#define ll long long
#define mod 1000000007
using namespace std;
const int maxn=2e5+5;
ll n,k,p;
ll kre(ll a,ll b){
a=a%p;
b=b%p;
return ((a*b-(ll)(((double)a*b+0.5)/p)*p)+p)%p;
}
ll kru(ll a,ll b){
ll ret=1;
while(b){
if(b&1){
ret=kre(ret,a)%p;
b--;
}
b/=2;
a=kre(a,a)%p;
}
return ret;
}
int main(){
int t;
ll ans;
scanf("%d",&t);
while(t--){
ans=0;
scanf("%lld %lld %lld",&n,&k,&p);
if(k==0) {
printf("%lld\n",n%p);
continue;
}
if(k%(p-1)==0)ans=kre(n/p,(p-1))%p;
n%=p;
for(ll i=1;i<=n;++i){
ans=(ans+kru(i,k))%p;
}
printf("%lld\n",(ans+p)%p);
}
return 0;
}