对于这题,做过好久了,但是重新翻出来的时候,脑袋又一脸懵逼了,看不懂自己的代码,自己默默的琢磨了一个晚上,还问了朋友。
题意:求解n以内与n不互质的数的个数
现在我来梳理下我那个晚上到底发生了什么。。。。。
刚开始,看到这题,我想的是,求解不互质个数=n-互质的个数
那这题就转换成求解互质的个数。
无奈当时数学不好啊,好气啊。
求解互质那第一步要先求出n的质因数,这个好办,我素数筛选下用数组存起来就好了。
然后求解互质个数,我暴力for一下,枚举n以内的数能不能整除这些素数
以下是这个思路的代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<math.h>
#define N 32770
using namespace std;
typedef long long LL;
bool prime[N];
int isprime[N],num=0;
vector<int> ans;
void getprime()
{
long long i,j,t;
for(i=2;i<=N-5;i++)
{
if(!prime[i])
{
isprime[++num]=i;
for(j=2;(t=i*j)<=N-5;j++)
{
prime[t]=true;
}
}
}
}
int main()
{
getprime();
int T;
scanf("%d", &T);
while(T--){
ans.clear();
int n;
scanf("%d", &n);
for(int i=1; i<=num; i++){
int x = isprime[i];
if(x>n)
break;
if(n%x==0)
ans.push_back(x);
}
int answer=0;
for(int i=1; i<n; i++){
bool flag=false;
for(int j=0; j<(int)ans.size(); j++){
int x=ans[j];
if(i%x==0){
flag=true;
break;
}
}
if(!flag)
answer++;
}
printf("%d\n", answer);
}
}
然而这个出现了一个问题,我两层for的时间复杂度极高,这样会不会超时呢?有什么更好的办法呢?
然后我去苦坑数学,我翻,我翻,我问,我问。。。
最后我知道了这个问题是欧拉函数。
是我们伟大的欧拉发现的!
对正整数n,欧拉函数是小于等于n的数中与n互质的数的数目。此函数以其首名研究者欧拉命名(Euler'so totientfunction),它又称为Euler's totient function、φ函数、欧拉商数等。 例如φ(8)=4,因为1,3,5,7均和8互质。
通式: φ(N)=N*(1-1/P1)*(1-1/P2)*...*(1-1/Pm)
其中p1, p2……pm为N的所有质因数,N是不为0的整数。
而我们从整数分解那里已经知道一个整数n可以分解为N=p1^a1*p2^a2……pm^am
代入得,φ(N)==p1^(a1-1)*(p1-1)*p2^(a2-1)*(p2-1)……pm^(am-1)*(pm-1)
那么,问题自然而然简单了,我们只要求出它分解之后的底数以及幂就行了,哈哈
下面有两种写法,你们自己看有啥区别吧~
1、正儿八经的写法
#include <iostream>
#include <cmath>
using namespace std;
#define MAXI 20
struct yinshu
{
int di;
int mi;
};
struct Div
{
int xiangshu;
int xdi[MAXI];
int xmi[MAXI];
};
Div getpN(int m);
void vout(Div pN);
yinshu getOne(int yin,int& m);
int main()
{
int ncase,n,i;
cin>>ncase;
Div pN;
while(ncase--)
{
cin>>n;
pN=getpN(n);
vout(pN);
}
return 0;
}
Div getpN(int m)
{
int i,j;
Div pans;
yinshu x;
if(m<2)
{
pans.xdi[0]=1;
pans.xmi[0]=0;
pans.xiangshu=0;
return pans;
}
i=2;
j=0;
for(i=2;i*i<=m;i++)
{
if(m%i==0)
{
x=getOne(i,m);
pans.xdi[j]=x.di;
pans.xmi[j]=x.mi;
j++;
}
}
if(m>1)
{
pans.xdi[j]=m;
pans.xmi[j]=1;
j++;
}
pans.xiangshu=j;
return pans;
}
void vout(Div pN)
{
int i,j;
int ans=1;
for(i=0;i<pN.xiangshu;i++)
{
for(j=1;j<=pN.xmi[i]-1;j++)
{
ans*=pN.xdi[i];
}
ans*=(pN.xdi[i]-1);
}
cout<<ans<<endl;
}
yinshu getOne(int yin,int& m)
{
yinshu x;
x.di=yin;
x.mi=0;
while(m%yin==0)
{
x.mi++;
m/=yin;
}
return x;
}
2、 思路大概一样啊,其实我也不是很清楚当时就这么做的,到现在也没怎么看懂,
如果你们看懂了好心人可以在评论区告诉我,thank you !
#include <iostream>
using namespace std;
#define MAXI 20
int fix(int n);
int Solve(int n);
int main()
{
int T,n;
cin>>T;
while(T--){
cin>>n;
cout<<n-Solve(n)<<endl;
}
return 0;
}
int fix(int n)
{
int ans=n;
int i;
for(i=2;i*i<=n;i++)
{
if(n%i==0)
{
ans=ans/i*(i-1);
while(n%i==0)
{
n/=i;
}
}
}
if(n>1)
ans=ans/n*(n-1);
return ans;
}
int Solve(int n)
{
int cnt=0;
int i;
for(i=1;i*i<=n;i++)
{
if(n%i==0)
{
if(i>=2)
cnt+=fix(n/i);
if((n/i)!=i && (n/i)>=2)
cnt+=fix(i);
}
}
return cnt;
}