题目链接:Hdu2588
—————————————-
概述
题目大意如下。
给定两个正整数
n
和
—————————————-
题解
将题目待求的答案设为Ans,则:
我们发现,上式能产生贡献的
i
均大于
所以我们可以只枚举
n
大于等于m的约数d,再判断1~n中有多少个数与
即:
这时我们发现,中括号里的表达式值为1时,
i
一定是
由于
i
一定是
至此,我们将式子转化成:
只需要 O(n√) 枚举满足条件的约数d,再 O(n√−−−√) 计算 φ(nd) 即可,总复杂度 O(n√⋅n√−−−√).
—————————————-
代码
#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 0x3f3f3f3f
using 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