欧拉函数——仪仗队
原文地址
发现这道题的题解大多都没有详细讲欧拉函数,所以本弱就来详细将讲欧拉函数
欧拉函数是小于
如何计算出
我会GCD暴力枚举!
复杂度
我会递推
复杂度
递推式:
我们有一个关于欧拉函数的性质,当(其实是我太菜了不会证明)若
所以与
其实根据上面那个定理,我们也可以得到另一个性质:当
那可不可以线性递推出
联想到我们是怎么筛出
不会的同学可以戳一戳
根据该链接的第一个算法,我们可以推出类比推出筛欧拉函数的方法
首先我们把所有的欧拉函数都设为它本身,然后再根据公式除以n所有的质因子(质因子可以靠筛法)
每当我们找到一个质数时,我们把它所有的倍数(即有质因子为该质数的数)乘以
以下算法复杂度为
il void work(int n)
{
for(re int i=1;i<=n;++i)
{
p[i]=i;
}
for(re int i=2;i<=n;++i)
{
if(p[i]==i)//如果i是质数
{
for(re int j=i;j<=n;j+=i)
{
p[j]=p[j]/i*(i-1);//那么就把i的所有倍数筛出来
}
}
}
}
既然我们可以利用第一种筛法求出所有欧拉函数,那么我们可不可以根据欧拉筛推出所有的欧拉函数呢?显然是可以的。
我们需要计算的东西其实和上面的方法一样,下面给出代码。
il void euler(int n)
{
p[1]=1;//1要特判
for(re int i=2;i<=n;++i)
{
if(!b[i])//这代表i是质数
{
prime[++num]=i;
p[i]=i-1;
}
for(re int j=1;j<=num&&prime[j]*i<=n;++j)//经典的欧拉筛写法
{
b[i*prime[j]]=1;//先把这个合数标记掉
if (i%prime[j]==0)
{
p[i*prime[j]]=p[i]*prime[j];//若prime[j]是i的质因子,则根据计算公式,i已经包括i*prime[j]的所有质因子
break;//经典欧拉筛的核心语句,这样能保证每个数只会被自己最小的因子筛掉一次
}
else
{
p[i*prime[j]]=p[i]*p[prime[j]];//利用了欧拉函数是个积性函数的性质
}
}
}
}
例题:P2158 [SDOI2008]仪仗队
通过观察发现,可以被看到的人必须满足
通过观察也可以发现,可以看到的人一定满足对称关系,所以我们只需要算出一个三角形(下面会提到)的对数在乘以
所以我们要算下图中为
0000
0001
0011
0111
那我们怎么求呢?显然可以用欧拉函数来求出与每一个数互质的数的个数即可。
具体实现:
我们先算出
为什么是
但这道题有一个坑点,如果
时间复杂度:
代码如下
#include<bits/stdc++.h>
using namespace std;
#define re register
#define maxn 400005
int n,ans,p[maxn];
int main()
{
cin>>n;
for(re int i=1;i<=n;++i)
{
p[i]=i;
}
for(re int i=2;i<=n;++i)
{
if(p[i]==i)
{
for(re int j=i;j<=n;j+=i)
{
p[j]=p[j]*(i-1)/i;
}
}
}
for(re int i=1;i<n;++i)
{
ans+=p[i];
}
printf("%d\n",(n==1)?0:ans<<1|1);
return 0;
}
后来学了莫比乌斯反演,发现这题还可以用莫比乌斯反演做。
题目所求即为:
按照莫比乌斯反演的套路,原式=
i,j同时除以g得:原式=
时间复杂度:
然后我们发现
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define re register
#define debug printf("Now is Line : %d\n",__LINE__)
#define file(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define mod 1000000007
il int read()
{
re int x=0,f=1;re char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=x*10+c-48,c=getchar();
return x*f;
}
#define maxn 40000
int mu[maxn+5],prim[maxn+5],vis[maxn+5],cnt,n,ans;
signed main()
{
n=read()-1;
if(!n) return puts("0"),0;
mu[1]=1;
for(re int i=2;i<=n;++i)
{
if(!vis[i]) prim[++cnt]=i,mu[i]=-1;
for(re int j=1;j<=cnt;++j)
{
if(prim[j]*i>n) break;
vis[prim[j]*i]=1;
if(i%prim[j]==0) break;
mu[i*prim[j]]=-mu[i];
}
}
for(re int i=1;i<=n;++i) mu[i]+=mu[i-1];
for(re int l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
ans+=(mu[r]-mu[l-1])*(n/l)*(n/r);
}
printf("%d",ans+2);
return 0;
}