神炎皇(???)题解【求互质数个数(欧拉函数)】

【问题描述】

神炎皇乌利亚很喜欢数对,他想找到神奇的数对。对于一个整数对(a,b),若满足a+b<=n且a+b是ab的因子,则成为神奇的数对。请问这样的数对共有多少呢?

【输入格式】

一行一个整数n。

【输出格式】

一行一个整数表示答案,保证不超过64位整数范围。

【样例输入输出】

input output
21 11

【数据范围与约定】

对于20%的数据n<=1000;
对于40%的数据n<=100000;
对于60%的数据n<=10000000;
对于80%的数据n<=1000000000000;
对于100%的数据n<=100000000000000。

【AC做法】

神炎皇(???)题解【求互质数个数(欧拉函数)】_第1张图片

解释1. a与b互质,那么(a+b)和a*b互质

证明:
假设a+b与a*b不互质,则它们有共同的一个因子m.
由m是a*b因子推得m是a或b或a和b的因子,又ab互质,所以只是a或b的因子.
m是a或b的因子又是a+b的因子可推出m是a和b的因子,与ab互质矛盾.
所以a+b与a*b互质

解释2. 欧拉函数

简介

φ(n)表示小于等于n的数中与n互质的数的数目。

基本性质

神炎皇(???)题解【求互质数个数(欧拉函数)】_第2张图片

线性筛法详解


1.若p为质数,则phi(p)=p-1(只有1与它互质);phi(1)=1。

2. 若i%p=0,则phi(i*p)=phi(i)*phi(p)(p为质数)

证明
设d=gcd(n,i),n、i不互质,则有n=xd,i=yd,所以n+i=(x+y)d,所以n+i与i不互质
[1,i]中与i不互质的数有i-phi(i)个,因为n+i与i不互质,所以[i+1,2*i]中与b不互质的数也是i-phi(i)个,所以[1,p*i]中与i不互质的数为p*i-p*phi(i)
因为i%p=0,所以i、p没有不相同的因数,所以[1,p*i]中与p*i不互质的数有p*i-phi(p*i)个
所以p*i-p*phi(i)=p*i-p*phi(i)
所以phi(p*i)=p*phi(i)。
以上过程证明了当n与i不互质时,n+i与i也不互质。

3. 若i mod p ≠0, 那么phi(i * p)=phi(i) * (p-1)

证明
i mod p 不为0且p为质数, 所以i与p互质, 那么根据欧拉函数的积性phi(i * p)=phi(i) * phi(p) 其中phi(p)=p-1即性质1

(以上参考Lytning的博客)


线性筛法代码

void getphi(int num)
{
    phi[1]=1;
    mark[1]=1;
    for (int i=2;i<=num;i++)
    {
        if (!mark[i]) //i是质数
        {
            phi[i]=i-1;
            prime[++c]=i;//用数组记录下质数
        }

        for (int j=1;j<=c;j++)
        {
            if (i*prime[j]>num) break;
            mark[i*prime[j]]=1; // i*prime[j]不是质数
            if(i%prime[j]==0)// 判断i是否是质数prime[j]的约数
            {
                phi[i*prime[j]]=phi[i]*prime[j];//计算phi(i*prime[j])只利用它的第一个质因数来计便可
                break;// 保证一个合数只被计算了一次,若没有break则会重复计算,没办法达到线性
            }else
                phi[i*prime[j]]=phi[i]*(prime[j]-1);
            // 利用欧拉函数的积性    
        }
    }
}

【AC代码】

#include
#include
#include
#include
#define LL long long
using namespace std;

const int maxn=10000005;
LL n,ans;
int m,c,phi[maxn],prime[maxn];
bool mark[maxn];

void getphi(int num)
{
    phi[1]=1;
    mark[1]=1;
    for (int i=2;i<=num;i++)
    {
        if (!mark[i]) 
        {
            phi[i]=i-1;
            prime[++c]=i;
        }

        for (int j=1;j<=c;j++)
        {
            if (i*prime[j]>num) break;
            mark[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];break;
            }else
                phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
    }
}

int main()
{

    scanf("%lld",&n);
    int m=sqrt(n);
    getphi(m);

    for(int i=2;i<=m;i++)
    {
        LL x=1LL*n/i/i;
        ans=ans+1LL*x*phi[i];
    }

    printf("%lld\n",ans);

    return 0;
}

【小技巧】

  1. 求数对(a,b)的个数,使满足(a+b)=k,且a,b互质,则满足条件的数对个数有phi(k)
  2. mod、整除等问题考虑先去掉gcd(a,b)

你可能感兴趣的:(NOIP,C++,题解,数论,欧拉函数)