B. Product
第一次写此类推导证明题目题解,就写详细点。
题意求:
a
n
s
=
∏
i
=
1
n
∏
j
=
1
n
∏
k
=
1
n
m
g
c
d
(
i
,
j
)
[
k
∣
g
c
d
(
i
,
j
)
]
m
o
d
p
(
n
,
m
,
p
<
2
e
9
,
且
p
为
质
数
)
ans=\prod_{i=1}^{n}\prod_{j=1}^{n}\prod_{k=1}^{n}m^{gcd(i,j)[k|gcd(i,j)]}\mod p (n,m,p<2e9,且p为质数)
ans=i=1∏nj=1∏nk=1∏nmgcd(i,j)[k∣gcd(i,j)]modp(n,m,p<2e9,且p为质数)
因为题目保证了p为质数,所以我们先求指数,然后在求指数的过程中对指数进行费马降幂,最后再对m快速幂即可。
a
n
s
=
m
m
i
m
o
d
(
p
−
1
)
m
o
d
p
ans=m^{mi\mod (p-1)}\mod p
ans=mmimod(p−1)modp
所以现在的关键是如何求
m
i
mi
mi,
a
n
s
ans
ans是累乘所以求
m
m
m的幂的时候就变成累加。
m
i
=
∑
i
=
1
n
∑
j
=
1
n
∑
k
=
1
n
g
c
d
(
i
,
j
)
[
k
∣
g
c
d
(
i
,
j
)
]
mi=\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{k=1}^{n}gcd(i,j)[k|gcd(i,j)]
mi=i=1∑nj=1∑nk=1∑ngcd(i,j)[k∣gcd(i,j)]
这个式子的大意是当
g
c
d
(
i
,
j
)
gcd(i,j)
gcd(i,j)是
k
k
k的倍数的时候
m
i
+
=
g
c
d
(
i
,
j
)
mi+=gcd(i,j)
mi+=gcd(i,j),我们改变枚举次序,把k移到外面,并且枚举k的倍数x。
m
i
=
∑
k
=
1
n
∑
k
∣
x
n
∑
i
=
1
n
∑
j
=
1
n
g
c
d
(
i
,
j
)
[
g
c
d
(
i
,
j
)
=
=
x
]
mi=\sum_{k=1}^{n}\sum_{k|x}^{n}\sum_{i=1}^{n}\sum_{j=1}^{n}gcd(i,j)[gcd(i,j)==x]
mi=k=1∑nk∣x∑ni=1∑nj=1∑ngcd(i,j)[gcd(i,j)==x]
m
i
=
∑
k
=
1
n
∑
k
∣
x
n
x
∑
i
=
1
n
∑
j
=
1
n
[
g
c
d
(
i
,
j
)
=
=
x
]
mi=\sum_{k=1}^{n}\sum_{k|x}^{n}x\sum_{i=1}^{n}\sum_{j=1}^{n}[gcd(i,j)==x]
mi=k=1∑nk∣x∑nxi=1∑nj=1∑n[gcd(i,j)==x]
m
i
=
∑
k
=
1
n
∑
k
∣
x
n
x
∑
i
=
1
n
/
x
∑
j
=
1
n
/
x
[
g
c
d
(
i
,
j
)
=
=
1
]
mi=\sum_{k=1}^{n}\sum_{k|x}^{n}x\sum_{i=1}^{n/x}\sum_{j=1}^{n/x}[gcd(i,j)==1]
mi=k=1∑nk∣x∑nxi=1∑n/xj=1∑n/x[gcd(i,j)==1]
式子中的
n
/
x
n/x
n/x默认是向下取整,对于
∑
i
=
1
n
/
x
∑
j
=
1
n
/
x
[
g
c
d
(
i
,
j
)
=
=
1
]
\sum_{i=1}^{n/x}\sum_{j=1}^{n/x}[gcd(i,j)==1]
∑i=1n/x∑j=1n/x[gcd(i,j)==1]可以用欧拉函数表示为
∑
i
=
1
n
/
x
2
ϕ
(
i
)
−
1
\sum_{i=1}^{n/x}2\phi(i)-1
∑i=1n/x2ϕ(i)−1,为什么呢?
看下面的图片应该可以明白,假设
n
/
x
=
5
n/x=5
n/x=5,那么
g
c
d
(
i
,
j
)
gcd(i,j)
gcd(i,j)就下面这样:
被重复加上了一次,所以等于
∑
i
=
1
n
/
x
2
ϕ
(
i
)
−
1
\sum_{i=1}^{n/x}2\phi(i)-1
∑i=1n/x2ϕ(i)−1,这部分可以用杜教筛求得。于是
m
i
=
∑
k
=
1
n
∑
k
∣
x
n
x
∑
i
=
1
n
/
x
2
ϕ
(
i
)
−
1
mi=\sum_{k=1}^{n}\sum_{k|x}^{n}x\sum_{i=1}^{n/x}2\phi(i)-1
mi=k=1∑nk∣x∑nxi=1∑n/x2ϕ(i)−1
下面我们来考虑怎么快速求出
∑
k
=
1
n
∑
k
∣
x
n
x
\sum_{k=1}^{n}\sum_{k|x}^{n}x
k=1∑nk∣x∑nx
=
∑
k
=
1
n
k
(
1
+
n
/
k
)
n
/
k
2
(
向
下
整
除
)
=\sum_{k=1}^{n}\frac{k(1+n/k)n/k}{2}(向下整除)
=k=1∑n2k(1+n/k)n/k(向下整除)
如果不太清除怎么变形过来的,可以看这里的例3:解释例3
这个算式可以分块在
O
(
n
)
O(\sqrt n)
O(n)求出
顺带提一下,比较常用的变形
(
∑
k
=
1
n
∑
k
∣
x
n
=
∑
x
=
1
n
x
d
(
x
)
)
,
d
(
x
)
是
x
的
约
数
个
数
(\sum_{k=1}^{n}\sum_{k|x}^{n}=\sum_{x=1}^{n}x d(x)),d(x)是x的约数个数
(k=1∑nk∣x∑n=x=1∑nxd(x)),d(x)是x的约数个数
有了这个变形,就可以在预处理的时候对
d
(
x
)
d(x)
d(x)进行预处理,对于没预处理到的部分我们就用
O
(
n
)
O(\sqrt n)
O(n)的分块算法计算,然后记忆化减少计算量。
这一部分可以用整除分块算出来。最终的式子就是这个:
m
i
=
∑
k
=
1
n
k
(
1
+
n
/
k
)
n
/
k
2
∑
i
=
1
n
/
x
2
ϕ
(
i
)
−
∑
k
=
1
n
k
(
1
+
n
/
k
)
n
/
k
2
mi=\sum_{k=1}^{n}\frac{k(1+n/k)n/k}{2}\sum_{i=1}^{n/x}2\phi(i) -\sum_{k=1}^{n}\frac{k(1+n/k)n/k}{2}
mi=k=1∑n2k(1+n/k)n/ki=1∑n/x2ϕ(i)−k=1∑n2k(1+n/k)n/k
代码如下:
#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));
}