1144 - 数论你还会快速幂
Time Limit:5s Memory Limit:256MByte
Submissions:430Solved:86
DESCRIPTION
今天HH
在学数论,他看到一个很优美的式子:
∑ni=1ik mod p
一向热衷于抱队友大腿的HH
便问队友ZZ怎么做
ZZ
:"n,k多大?"
HH
:"105,105"
ZZ
:"快速幂嘛"
HH
:"109,105"
ZZ
:"拉格朗日插值嘛"
HH
:"1018,1018"
队友:"让我想想.."
INPUT
第一行是一个整数T(1≤T≤1000)
是质数,
OUTPUT
对于每组数据输出题目中的表达式的值
SAMPLE INPUT
25 2 37 2 3
SAMPLE OUTPUT
12
题解:
看到n比p大了不到100个,我们可以思考一下假如我们只算到p或者p−1
反正我是不知道怎么去找这个规律,但是我们知道,i^k%p和(i+p)^k%p的价值是一样的。
题解:
看到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)快速乘
反正我是不知道怎么去找这个规律,但是我们知道,i^k%p和(i+p)^k%p的价值是一样的。
那么肯定以长度p为循环节,是有循环内容的。这个就当做一个结论来记住吧,。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const ll INFF=0x3f3f3f3f3f3f3f3f;
const double pi=acos(-1.0);
const double eps=1e-9;
ll n,k,p;
ll qsum(ll a,ll b)
{
ll ans=0;
while(b)
{
if(b&1)
{
ans=(ans+a)%p;
}
a=(a+a)%p;
b/=2;
}
return ans;
}
ll qpow(ll a,ll b)
{
a%=p;
ll ans=1;
while(b)
{
if(b&1)
{
ans=(qsum(ans,a))%p;
}
b>>=1;
a=qsum(a,a)%p;
}
return ans;
}
int main()
{
int t;scanf("%d",&t);
while(t--)
{
scanf("%lld%lld%lld",&n,&k,&p);
if(k==0)
{
printf("%lld\n",n%p);
continue;
}
ll sum=0;
if(k%(p-1)==0)
{
sum=(sum+qsum(n/p,p-1))%p;
}
n%=p;
for(ll i=1;i<=n;i++)
{
sum=(sum+qpow(i,k))%p;
}
printf("%lld\n",(sum+p)%p);
}
return 0;
}