前言
欧拉函数φ(n)是小于n的正整数中与n互质的数的数目(φ(1)=1),
φ(n)称为n的欧拉值
例如φ(8)=4(2,3,5,7)。
公式如下
φ
(
x
)
=
x
∏
i
=
1
n
(
1
−
1
p
i
)
\varphi (x)=x\prod_{i=1}^{n}(1-\frac{1}{p_i})
φ(x)=x∏i=1n(1−pi1)
也就是
φ
(
x
)
=
x
(
1
−
1
p
1
)
(
1
−
1
p
2
)
⋅
⋅
⋅
⋅
(
1
−
1
p
n
)
\varphi (x)=x(1-\frac{1}{p_1})(1-\frac{1}{p_{2}})\cdot \cdot \cdot \cdot (1-\frac{1}{p_{n}})
φ(x)=x(1−p11)(1−p21)⋅⋅⋅⋅(1−pn1)
其中pi为x的所有质因数,x不为零。
注意:每种质因数只一个。
欧拉函数有如下性质:
- 若n是质数p的k次幂,则
φ
(
x
)
=
p
k
−
p
k
−
1
=
(
p
−
1
)
p
k
−
1
\varphi (x)=p^{k}-p^{k-1}=(p-1)p^{k-1}
φ(x)=pk−pk−1=(p−1)pk−1
因为除了p的倍数外,其他数都跟n互质。 - 欧拉函数是非完全积性函数:若m,n互质,则 φ ( m ∗ n ) = φ ( m ) ∗ φ ( n ) \varphi (m*n)=\varphi (m)*\varphi (n) φ(m∗n)=φ(m)∗φ(n)
- 当n为奇质数时, φ ( 2 n ) = φ ( n ) \varphi (2n)=\varphi (n) φ(2n)=φ(n)
- 若n为质数,则 φ ( n ) = n − 1 \varphi (n)=n-1 φ(n)=n−1
- 若a为素数,b mod a=0,则 φ ( a ∗ b ) = a ∗ φ ( b ) \varphi (a*b)=a*\varphi (b) φ(a∗b)=a∗φ(b)
- 当n>2时,φ(n)是偶数。
- 小于n的数中,与n互质的数的总和为: n φ ( n ) 2 \frac{n\varphi (n)}{2} 2nφ(n) (n>1)。
- n = ∑ d ∣ n φ ( d ) n=\sum_{d\mid n}φ(d) n=∑d∣nφ(d),即n的因数(包括1和它自己)的欧拉函数之和等于n
实现
如何求1到n区间内所有数的欧拉值,下面代码是基于线性素数筛法(传送门)
#include<stdio.h>
#include<string.h>
int prime[10000],ans=0;//存放素数的数组
bool sign[10000];//标记,0为素数,1不为素数
int euler[10000];//欧拉值数组,初始化为1
void Euler(int n){
memset(sign,0,sizeof(sign));
euler[1]=1;//φ(1)=1
for(int i=2;i<=n;i++){
if(!sign[i]){//判断是否标记,未标记的为素数
prime[ans++]=i;//保存素数i
euler[i]=i-1;//性质四
}
for(int j=0;j<ans&&i*prime[j]<=n;j++) {
sign[prime[j]*i]=1;//i*prime[j]不为素数,标记它
if(i%prime[j]==0){//看p[j]是否是i的约数,因为p[j]为素数,等于判断i和p[j]是否互质
euler[prime[j]*i]=euler[i]*prime[j];//性质五
break;
}else{
euler[prime[j]*i]=euler[i]*(prime[j]-1); //性质二
}
}
}
}
void show(int n){
for(int i=1;i<=n;i++){
printf("%d ",euler[i]);
}
}
int main(){
int n;
scanf("%d",&n);
Euler(n);
show(n);
return 0;
}
练习
看完上面的解析,想必已经跃跃欲试了,现在上题
首先发出题目链接:
链接:https://ac.nowcoder.com/acm/contest/317/D
来源:牛客网
涉及:欧拉函数
看完题目,首先不难发现,若gcd(n, x) = 1,那么gcd(n, n − x)一定等于1
证明:反证法,gcd(n,x)=gcd(n,n-x)=k
若
{
n
=
k
⋅
p
n
−
x
=
k
⋅
q
\left\{\begin{matrix} n=k\cdot p\\ n-x=k\cdot q \end{matrix}\right.
{n=k⋅pn−x=k⋅q,那么x=k * p-k * q=k * (p-q),那么n,x都有k这个约数,与假设冲突。
同时不难发现,若小a在某个位置获得了
k
x
k^{x}
kx的贡献,那么一定存在一个位置会获得
k
n
−
x
k^{n-x}
kn−x的贡献,而且两个人的贡献是相同的!
把小a的贡献单独写出来即为
A
∗
k
a
∗
k
n
−
a
∗
k
b
∗
k
n
−
b
⋅
⋅
⋅
⋅
=
A
∗
k
R
n
A\ast k^{a}\ast k^{n-a}\ast k^{b}\ast k^{n-b}\cdot \cdot \cdot \cdot =A \ast k^{Rn}
A∗ka∗kn−a∗kb∗kn−b⋅⋅⋅⋅=A∗kRn,
考虑如何快速得到R的值,观察题目描述不难发现,能产生答案的数一定是与n互质的数,这与ϕ函数的定义是相同的!
又因为前n个数的欧拉函数之和为
n
φ
(
n
)
2
\frac{n\varphi (n)}{2}
2nφ(n)。
因此最终的答案为
(
A
+
B
)
∗
K
n
φ
(
n
)
2
(A+B) \ast K^{\frac{n\varphi (n)}{2}}
(A+B)∗K2nφ(n)
AC代码如下
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll oula(ll n){//欧拉函数求互质数的个数
ll i,ans=n;
for(i=2;i*i<=n;i++){
if(n%i==0){
ans=ans-ans/i;
do{
n/=i;
}while(n%i==0);
}
}
if(n>1){
ans=ans-ans/n;
}
return ans;
}
ll pl(ll sum,ll p){
ll ans=1;
while(p){
if(p&1){
ans=ans*sum%1000000007;
}
sum=sum*sum%1000000007;
p>>=1;
}
return ans;
}
int main(){
ll n,k,a,b,p,ans;
cin >> n >> k >> a >> b;
p=oula(n);
ll sum=0;
p=p/2*(n);
sum=a+b;
ans=pl(k,p);
cout << ans*sum%1000000007;
return 0;
}