题目链接:传送门
题目需求:Given integers N and M, how many integer X satisfies 1<=X<=N and (X,N)>=M.(2<=N<=1000000000, 1<=M<=N),
题目解析:
求(X,N),不用想要分解N的因子,分解方法如下,我一开始直接分解for(int i=2;i<=n/2;i++),这样的话如果n==10^9,那么直接超时,因为这点失误直接浪费了一中午
的时间,要这么分解for(int i=2;i*i<=n;i++)具体请在代码里面看,然后开始求(X,N)>=M。
这才是核心:
要求有多少个 i 满足gcd(i, N) = d(1<=i<=N)
如果gcd(i, N) = d,则gcd(i/d, N/d) = 1
由于i <= N,所以 i/d <= N/d,转化为求多少个不大于N/d的数与N/d互质,而这就是欧拉函数
所以有phi(N/d)个 i 满足gcd(i, N) = d,所以求gcd(i,N)>=M,就是求N的因子中大于等于M的欧拉函数值,
即gcd(N/d1)+gcd(N/d2)+...+gcd(N/dn),其中di>=M,且为N的因子。
直接写:(都是15ms,这是后台数据的问题,数据多了肯定还是打表快)
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
typedef __int64 ll;
using namespace std;
ll n,m,sum,top,key,M,coun,i;
int f[1001];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
sum=1;
top=0;
scanf("%I64d%I64d",&n,&m);
for(i=2; i*i<n; i++)
{
if(n%i==0)
{
f[top++]=i;
f[top++]=n/i;
}
}
if(i*i==n)//千万别忘了这一句,如16=4*4
{
f[top++]=i;
}
sort(f,f+top);
key=-1;
for(i=0; i<top; i++)
{
if(f[i]>=m)
{
key=i;
break;
}
}
if(key==-1)
{
printf("1\n");
continue;
}
for(i=key; i<top; i++)
{
M=n/f[i];
coun=n/f[i];
for(ll z=2; z*z<=M; z++)
{
if(M%z==0)
{
coun-=coun/z;
M/=z;
while(M%z==0)
M/=z;
}
}
if(M!=1) coun-=coun/M;
sum+=coun;
}
printf("%I64d\n",sum);
}
return 0;
}
一部分欧拉值打表:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
typedef __int64 ll;
using namespace std;
ll n,m,sum,top,key,i;
int phi[10006],f[1001];
void init()
{
memset(phi,0,sizeof(phi));
phi[1]=1;
for(int i=2; i<=10000; i++)
{
if(!phi[i])
{
for(int j=i; j<=10000; j=j+i)
{
if(!phi[j]) phi[j]=j;
phi[j]-=phi[j]/i;
}
}
}
}
int main()
{
int T;
scanf("%d",&T);
init();
while(T--)
{
sum=1;
top=0;
scanf("%I64d%I64d",&n,&m);
for(i=2; i*i<n; i++)
{
if(n%i==0)
{
f[top++]=i;
f[top++]=n/i;
}
}
if(i*i==n)
{
f[top++]=i;
}
sort(f,f+top);
for(i=0; i<top; i++)
{
if(f[i]>=m)
{
key=i;
break;
}
}
if(key==-1)
{
printf("1\n");
continue;
}
for(ll i=key; i<top; i++)
{
if(n/f[i]<=10000)
{
sum+=phi[n/f[i]];
continue;
}
ll M=n/f[i];
ll coun=n/f[i];
for(ll z=2; z*z<=M; z++)
{
if(M%z==0)
{
coun-=coun/z;
M/=z;
while(M%z==0)
M/=z;
}
}
if(M!=1) coun-=coun/M;
sum+=coun;
}
printf("%I64d\n",sum);
}
return 0;
}