Luogu P2158仪仗队
欧拉函数
\(1\)~\(n\)中与\(n\)互质的数的个数被称为欧拉函数,记为\(\varphi(n)\)
\[\varphi(n)=n*\frac{p_{1}-1}{p_{1}} * \frac{p_{2}-1}{p_{2}} * ... * \frac{p_{m}-1}{p_{m}}=n*\prod_{质数p|n}^{} (1-\frac{1}{p})
\]
根据欧拉函数的计算式,在分解质因数的时候求出欧拉函数即可:
int phi_(int n){
int res=n;
for(int i=2;i<=sqrt(n);i++){
if(n%i==0){
res=res/i*(i-1);
while(n%i==0) n/=i;
}
}
if(n>1) res=res/n*(n-1);
return res;
}
题解
题目
自己推一下很容易发现,一个点\((x,y)\)能被看到,当且仅当\(gcd(x,y)=1\)
又因为这是一个方阵,能看到的点关于\((1,1)\)到\((n,n)\)的对角线是对称的,所以求出一半的点的数量\(ans\)(不包括\((1,1)\)),\(ans\times2+1\)即为最终结果
- 为什么加和的时候是\(1\)~\((n-1)\)?
因为点\((1,1)\)是不包括的,三角形只有\((n-1)\)排 - 为什么要特判\(n==1\)?
因为只有一个点\((1,1)\)的时候就是他看到的就是0个啊
code
纯欧拉函数
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#define maxn 40010
using namespace std;
template<typename T>
inline void read(T &x){
x=0; bool flag=0; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
for(;isdigit(c);c=getchar()) x=x*10+(c^48);
if(flag) x=-x;
}
int n;
int ans,phi[maxn];
int phi_(int n){
int res=n;
for(int i=2;i<=sqrt(n);i++){
if(n%i==0){
res=res/i*(i-1);
while(n%i==0) n/=i;
}
}
if(n>1) res=res/n*(n-1);
return res;
}
int main(){
read(n);
if(n==1){cout<<0<<endl; return 0;}
for(int i=1;i<=n;i++) phi[i]=phi_(i);
for(int i=1;i<=n-1;i++) ans+=phi[i];
cout<<2*ans+1<<endl;
return 0;
}
Eratosthenes筛法
\(O(n \ log \ n)\)
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#define maxn 40010
using namespace std;
template<typename T>
inline void read(T &x){
x=0; bool flag=0; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
for(;isdigit(c);c=getchar()) x=x*10+(c^48);
if(flag) x=-x;
}
int n;
int ans,phi[maxn];
void phi_(int n){
for(int i=1;i<=n;i++) phi[i]=i;
for(int i=2;i<=n;i++){
if(phi[i]==i){
for(int j=i;j<=n;j+=i){
phi[j]=phi[j]/i*(i-1);
}
}
}
}
int main(){
read(n);
if(n==1){cout<<0<<endl; return 0;}
phi_(n);
for(int i=1;i<=n-1;i++) ans+=phi[i];
cout<<2*ans+1<<endl;
return 0;
}
//Eratosthenes筛法,O(n log n)
线性筛
\(O(n)\)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#define maxn 40010
using namespace std;
template<typename T>
inline void read(T &x){
x=0; bool flag=0; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
for(;isdigit(c);c=getchar()) x=x*10+(c^48);
if(flag) x=-x;
}
int n;
int ans,cnt,mindiv[maxn],isprime[maxn],phi[maxn];
void phi_(int n){
cnt=0;//质数数量
memset(mindiv,0,sizeof(mindiv));//最小质因子
phi[1]=1;
for(int i=2;i<=n;i++){
if(mindiv[i]==0){//i是质数
mindiv[i]=i;
isprime[++cnt]=i;
phi[i]=i-1;
}
for(int j=1;(j<=cnt)&&(i*isprime[j]<=n);j++){
mindiv[i*isprime[j]]=isprime[j];
phi[i*isprime[j]]=phi[i]*(i%isprime[j]?isprime[j]-1:isprime[j]);//"isprime[j]-1" and "phi[isprime[j]]" both right
if(mindiv[i]==isprime[j]) break;
}
}
}
int main(){
read(n);
if(n==1){cout<<0<<endl; return 0;}
phi_(n);
for(int i=1;i<=n-1;i++) ans+=phi[i];
cout<<2*ans+1<<endl;
return 0;
}
//线性筛,O(n)