[HDU2588]GCD 欧拉函数

来源:互联网 发布:php 手机号地区 编辑:程序博客网 时间:2024/06/13 15:37

题目链接:Hdu2588

—————————————-

概述

题目大意如下。

给定两个正整数nm,(2n10000000001mn),问有多少个x满足1xn gcd(n,x)m。题目有多组数据。

—————————————-

题解

将题目待求的答案设为Ans,则:

Ans=i=1n[gcd(n,i)m].

我们发现,上式能产生贡献的i均大于m,能产生贡献的gcd(n,i)均为n的约数。

所以我们可以只枚举n大于等于m的约数d,再判断1~n中有多少个数与n的最大公约数为d

即:

Ans=mdn, d|ni=1n [gcd(n,i)=d].

这时我们发现,中括号里的表达式值为1时,i一定是d的倍数,所以我们进一步转化:

mdn, d|ni=1n [gcd(n,i)=d]= mdn, d|ni=1n[gcd(nd,id)=1]×[d|i].

由于i一定是d的倍数,所以id的取值范围是1~nd,所以上式中ni=1[gcd(nd,id)=1]×[d|i] 实际上就是在统计1~nd中与nd互质的数的个数,其实就是φ(nd).

至此,我们将式子转化成:

mdn, d|ni=1n[gcd(nd,id)=1]×[d|i]=mdn, d|nφ(nd).

只需要O(n)枚举满足条件的约数d,再O(n)计算φ(nd)即可,总复杂度O(nn).

—————————————-

代码

#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>#define ll long long#define For(i,j,k) for(register ll i=j; i<=(ll)k; ++i)#define Forr(i,j,k) for(reggister ll i=j; i>=(ll)k; --i)#define INF 0x3f3f3f3fusing namespace std;ll T, n, m, phi, Ans;inline ll get_phi(ll x){    ll back = x;    for(register ll i=2; i*i <= x; ++i)        if(x%i == 0){            back = back/i*(i-1);            while(x%i == 0)                x /= i;        }    if(x != 1)        back = back/x*(x-1);    return back;}//质因数分解求欧拉函数。 int main(){    scanf("%lld", &T);    while(T --){        scanf("%lld%lld", &n, &m);        Ans = 0;        for(ll di=1; di*di<=n; ++di)            if(n%di == 0){//枚举约数n/d。                 ll d = n/di; //计算d。                 if(d < m)   break;//考虑到我们得到的d是单调递减的,假如当前d不满足条件,直接退出。                 phi = get_phi(di);                Ans += phi;//计算答案                 if(d*d == n)    continue;//特判d为根号n的情况。                 if(di >= m){                    phi = get_phi(d);                    Ans += phi;//假如n/d也满足条件,计算答案。                 }            }        printf("%lld\n", Ans);    }    return 0;}

—————————————-

小结

本题重点在于gcd和欧拉函数之间的转化,将枚举gcd(n,i)=d 转变成枚举与nd互质的数是关键,偷换概念也是很实用的方法。

—————————————-

wrote by miraclejzd

原创粉丝点击