B. Product
第一次寫此類推導證明題目題解,就寫詳細點。
題意求:
因爲題目保證了p爲質數,所以我們先求指數,然後在求指數的過程中對指數進行費馬降冪,最後再對m快速冪即可。
所以現在的關鍵是如何求,是累乘所以求的冪的時候就變成累加。
這個式子的大意是當是的倍數的時候,我們改變枚舉次序,把k移到外面,並且枚舉k的倍數x。
式子中的默認是向下取整,對於可以用歐拉函數表示爲,爲什麼呢?
看下面的圖片應該可以明白,假設,那麼就下面這樣:
被重複加上了一次,所以等於,這部分可以用杜教篩求得。於是
下面我們來考慮怎麼快速求出
如果不太清除怎麼變形過來的,可以看這裏的例3:解釋例3
這個算式可以分塊在求出
順帶提一下,比較常用的變形
有了這個變形,就可以在預處理的時候對進行預處理,對於沒預處理到的部分我們就用的分塊算法計算,然後記憶化減少計算量。
這一部分可以用整除分塊算出來。最終的式子就是這個:
代碼如下:
#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
typedef long long ll;
const int N=5e6;
int n,m,mod,tot=0;
bool vis[N];
int p[N/10],phi[N],sum[N];// phi(x),x*d(x)的前綴和
unordered_map<int, int>M_phi,M_sum;
void add(int &x,int y){//手寫模運算防止卡常數
if(y<0)x+=y;
else x=x-mod+y;
if(x<0)x+=mod;
}
void init(){
phi[1]=sum[1]=1;
for(int i=2;i<N;i++){
if(!vis[i]){
sum[i]=2;
phi[i]=i-1;
p[tot++]=i;
}
for(int j=0;j<tot&&i*p[j]<N;j++) {
int v=i*p[j];
vis[v]=1;
if(i%p[j]==0){
phi[v]=phi[i]*p[j];
int d=1,x=i;
while(x%p[j]==0)
x/=p[j],d++;
sum[v]=sum[i]/d*(d+1);
break;
}
phi[v]=phi[i]*(p[j]-1);
sum[v]=sum[i]*2;
}
}
for(int i=1;i<N;i++){
add(phi[i],phi[i-1]);
sum[i]=1ll*sum[i]*i%mod;
add(sum[i],sum[i-1]);
}// phi(x),x*d(x)的前綴和
}
int S_phi(int x){
if(x<N)return phi[x];
if(M_phi[x])return M_phi[x];
int ret=0;
for(ll l=2,r;l<=x;l=r+1)
r=x/(x/l),add(ret,S_phi(x/l)*(r-l+1)%mod);
return M_phi[x]=((ll)x*(x+1)/2-ret+mod)%mod;
}
int S_sum(int x){
if(x<N)return sum[x];
if(M_sum[x])return M_sum[x];
int res=0;
for(int l=1,r;l<=x;l=r+1){
r=x/(x/l);
add(res,(1ll*(l+r)*(r-l+1)/2 %mod *1ll*(1+x/l)*(x/l)/2% mod)% mod);
}
return M_sum[x]=res;
}
int fp(int x,int y){
int ret=1;
while(y){
if(y&1)ret=1ll*ret*x%mod;
x=1ll*x*x%mod;
y/=2;
}
return ret;
}
int main(){
scanf("%d%d%d",&n,&m,&mod);
mod--; //費馬降冪
init();
int ans=0;
for(int l=1,r;l<=n;l=r+1){
r=n/(n/l);
int temp=S_sum(r);
add(temp,-S_sum(l-1));
add(ans,1ll*S_phi(n/l)*temp%mod);
}
add(ans,ans),add(ans,-S_sum(n));
mod++;
printf("%d\n",fp(m,ans));
}