-
容斥原理:
- N中的互质数个数等于
- N-N/p1-N/p2-…-N/pkN/pk代表N中pk的倍数的个数,这样就会发现,如果一个数即是p1的倍数又是p2的倍数,那么这个数就会被减去两次,所以下一步要
- +N/(p1 * p2)+N/(p2 * p3)+…+… 如果出现三次又会加上三次,相当于没减,而我们要减去一次,所以要
- -N/(p1 * p2 * p3)-…-…
- …
- 这就是容斥原理,减奇数加偶数,一直到最后
-
欧拉函数
- 欧拉函数在其基础上,把容斥原理的式子进行整理,得出O(N)=N * (1-1/p1) * (1-1/p2)… * (1-1/pk) 展开后就是容斥原理的式子,上面式子被称为欧拉函数,仍然基于分解质因数实现
- gcd(a,b)=1称为互质数
- 比如24中的互质数1,5,7,11,13,17,19,23=8
- O(24)=24 * (1-1/2) * (1-1/3)=8
- 公式法求欧拉函数
#include<iostream>
using namespace std;
int main()
{
int n;cin>>n;
while(n--)
{
int x;cin>>x;
int ans=x;
for(int i=2;i<=x/i;i++)
{
if(x%i==0)
{
ans=ans/i*(i-1);
while(x%i==0)x/=i;
}
}
if(x>1)ans=ans/x*(x-1);
cout<<ans<<endl;
}
return 0;
}
- 筛法求欧拉函数
- 在线性筛素数的过程中把该点的欧拉值更新
- 如果i是素数那么ol[i]=i-1
- 如果i不是素数prims[j]是i的质因数,所以ol[prims[j]*i]=ol[i] * prims[j]
- 如果i不是素数prim[j]不是i的质因数
- ol[prims[j]*i]=ol[i] * (prims[j]-1)
- 这样更新之后就在线性筛的过程中将每个数的欧拉函数更新
#include<iostream>
using namespace std;
const int N=1e6+10;
typedef long long ll;
bool st[N];
int prims[N],ols[N];
ll ol(int n)
{
int cnt=0;
ll res=0;
for(int i=2;i<=n;i++)
{
if(!st[i])
{
prims[cnt++]=i;
ols[i]=i-1;
}
for(int j=0;prims[j]<=n/i;j++)
{
st[prims[j]*i]=true;
if(i%prims[j]==0)
{
ols[prims[j]*i]=prims[j]*ols[i];
break;
}
ols[prims[j]*i]=(prims[j]-1)*ols[i];
}
}
for(int i=1;i<=n;i++)
{
res+=ols[i];
}
return res;
}
int main()
{
int n;
cin>>n;
cout<<ol(n)+1<<endl;
return 0;
}