Problem Description
给出n,m,p,求 (1^m + 2^m + 3^m + 4^m + ... + n^m) % p
Input
第一行一个数T( <= 10),表示数据总数
然后每行给出3个数n,m,p(1 <= n <= m <= 10^18, 1 <= p <= 10^6, p是质数
Output
每组数据输出你求得的结果
Sample Input
2 1 1 11 3 2 11
Sample Output
1 3
Hint
i^m即求 i * i * i * i * i... * i(m个i),比如2^3即2 * 2 * 2
分析,首先n很大,但是这个求和需要模p,所以我们可以考虑将求和范围压缩在0~p-1中间,然后求出结果以后乘上倍数再加上剩余部分就可以了。但是这样时间复杂度还会是很大。这里还可以用欧拉定理来优化。
n,a为正整数,且n,a互质,则:
Power Sum
因为这里我们已经已经将求和范围压缩在0~p-1里面了,同时因为p是一个质数,所以我们知道1~p-1的数都符合上述的公式。对于求次幂,我们可以用快速幂,这样问题就基本解决了。这就是标准解法。问题是我用这种方法结果还是超时了,主要问题好像是取模太多了。
于是我又想出另一种解法对于每一个数都可以分解成素数相乘,那我们先压缩数据在0~p-1之间,然后就可以将这些数分解成质数的幂相乘,然后我们可以想素数筛法那样求一遍,最终就可以得到结果。这种方法之所以可以使用是因为10^6一下的质数大概有1.2*10^5这么多个,不算多,所以可行。同时这种方法相对于前面的方法速度上应该会更快。前一种方法的时间复杂度大概是O(p*logp),后一种方法的时间复杂度大概是O(ploglogp),接近O(p)。
上代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #define LL long long 5 #define MAX 1000002 6 using namespace std; 7 8 LL n,m,p,ti; 9 10 11 bool f[MAX]; 12 LL mm[MAX]; 13 LL ans,rr,r; 14 15 LL Fast_Mod(LL i,LL t){ 16 LL a = 1; 17 while(t){ 18 if(t&1) a = i*a%p; 19 i = (i%p)*(i%p)%p; 20 t>>=1; 21 } 22 return a; 23 } 24 25 void solve1(){ 26 ans=0; 27 memset(f,0,sizeof(f)); 28 for(int i=1;i<=p;i++) mm[i]=1; 29 LL u; 30 f[1]=1; 31 for(LL i=1;i<=p;i++){ 32 if(!f[i]){ 33 mm[i] = Fast_Mod(i,m); 34 for(LL j=i+i;j<=p;j+=i){ 35 f[j]=1; 36 u = j; 37 while(u%i==0){ 38 u/=i; 39 mm[j]=mm[j]*mm[i]%p; 40 } 41 } 42 } 43 ans = (ans + mm[i])%p; 44 if(i<=r) rr=ans; 45 } 46 } 47 48 void solve2(){ 49 ans=0; 50 memset(f,0,sizeof(f)); 51 for(int i=1;i<=r;i++) mm[i]=1; 52 LL u; 53 f[1]=1; 54 for(LL i=1;i<=r;i++){ 55 if(!f[i]){ 56 mm[i] = Fast_Mod(i,m); 57 for(LL j=i+i;j<=r;j+=i){ 58 f[j]=1; 59 u = j; 60 while(u%i==0){ 61 u/=i; 62 mm[j]=mm[j]*mm[i]%p; 63 } 64 } 65 } 66 ans = (ans + mm[i])%p; 67 } 68 } 69 70 71 int main() 72 { 73 int t; 74 //freopen("data.txt","r",stdin); 75 scanf("%d",&t); 76 while(t--){ 77 scanf("%lld %lld %lld",&n,&m,&p); 78 ti = n/p; 79 r = n%p; 80 rr=0; 81 if(ti!=0){ 82 solve1(); 83 ans = (ans*ti)%p; 84 ans = (ans+rr)%p; 85 }else{ 86 solve2(); 87 } 88 printf("%lld\n",ans); 89 } 90 return 0; 91 }