HDU 2588 欧拉函数

题目链接

题意:
给定两个整数 N N N, M M M,求有多少个整数 X X X( 1 ≤ X ≤ N 1\leq X \leq N 1XN),满足 M ≤ g c d ( X , N ) M \leq gcd(X, N) Mgcd(X,N)

思路:

题意很简单,思路却有点难想。

首先 N N N是一个已知的整数,而 g c d ( X , N ) gcd(X,N) gcd(X,N)一定是 N N N的一个约数
N N N的约数并不会很多,故我们可以逐一枚举,并找到每一个不小于 M M M的约数,此处设该约数为P
则:
P ∣ N P|N PN M ≤ P M \leq P MP

而对于枚举出的每一个P,原问题得到了简化:
对于一个已知的整数P,求有多少个整数 X X X( 1 ≤ X ≤ N 1\leq X \leq N 1XN),满足 g c d ( X , N ) = P gcd(X, N) = P gcd(X,N)=P

想到这里,解法其实离我们已经很近了。
让我们设简化后的问题答案为 A n s Ans Ans
则:
A n s = ∑ i = 1 N [ g c d ( i , N ) = = P ] Ans = \sum_{i=1}^{N}[gcd(i, N) == P] Ans=i=1N[gcd(i,N)==P]
= ∑ i = 1 ⌊ N P ⌋ [ g c d ( i ∗ P , N ) = = P ] = \sum_{i=1}^{\lfloor\frac{N}{P}\rfloor} [gcd(i*P, N) == P] =i=1PN[gcd(iP,N)==P]
= ∑ i = 1 ⌊ N P ⌋ [ g c d ( i , ⌊ N P ⌋ ) = = 1 ] = \sum_{i=1}^{\lfloor\frac{N}{P}\rfloor} [gcd(i, \lfloor\frac{N}{P}\rfloor) == 1] =i=1PN[gcd(i,PN)==1]

显然,上面的式子正是欧拉函数的定义式
φ ( ⌊ N P ⌋ ) \varphi(\lfloor\frac{N}{P}\rfloor) φ(PN)

对于不太理解欧拉函数的朋友,可以接着看下面的推导:

回到刚才的式子:
A n s = ∑ i = 1 ⌊ N P ⌋ [ g c d ( i , ⌊ N P ⌋ ) = = 1 ] Ans = \sum_{i=1}^{\lfloor\frac{N}{P}\rfloor} [gcd(i, \lfloor\frac{N}{P}\rfloor) == 1] Ans=i=1PN[gcd(i,PN)==1]

基于容斥的思想,我们引入艾弗森约定:
∑ d ∣ n μ ( d ) = ( n = = 1 ) \sum_{d|n}\mu(d) = (n == 1) dnμ(d)=(n==1)
假设: T = ⌊ N P ⌋ T = \lfloor\frac{N}{P}\rfloor T=PN

故原式可转化为:
A n s = ∑ i = 1 T [ g c d ( i , T ) = = 1 ] Ans = \sum_{i=1}^{T}[gcd(i,T)==1] Ans=i=1T[gcd(i,T)==1]
= ∑ i = 1 T ∑ d ∣ g c d ( i , T ) μ ( d ) =\sum_{i=1}^T\sum_{d|gcd(i,T)} \mu(d) =i=1Tdgcd(i,T)μ(d)
= ∑ d ∣ T μ ( d ) ∗ T d =\sum_{d|T} \mu(d)* \frac{T}{d} =dTμ(d)dT

由公式:
∑ d ∣ T μ ( d ) d = φ ( T ) T \sum_{d|T}\frac{\mu(d)}{d} = \frac{\varphi(T)}{T} dTdμ(d)=Tφ(T)

最后推出:
A n s = φ ( T ) Ans = \varphi(T) Ans=φ(T)
此题得解。


代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

ll euler(ll x){
    ll res = x;
    for(int i=2 ;i*i<=x ;i++){
        if(x%i == 0){
            res = res/i*(i-1);
            while(x%i==0) x/=i;
        }
    }
    if(x>1) res = res/x*(x-1);
    return res;
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        ll n,m;
        scanf("%I64d%I64d",&n,&m);

        ll ans = 0;
        for(ll i=1 ;i*i<=n ;i++){
            if(n%i == 0){
                if(i >= m){
                    ans += euler(n/i);
                }
                if((n/i)>=m && n/i != i){
                    ans += euler(i);
                }
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值