题目描述
题目大意:0 < <script type="math/tex" id="MathJax-Element-7"><</script>a<=A,0 < <script type="math/tex" id="MathJax-Element-8"><</script>b<=B,问有多少有序数对(a, b)满足gcd(a, b)=d。A,B<=10^6。
题解
这是一道适合底层数论选手切的数论“扫盲”题。
题目要求(假设
a<=b
)
∑i=1a∑j=1b[(i,j)=k]
就是
∑i=1a∑j=1b[k|i][k|j][(ik,jk)=1]
根据社会主义套路,枚举倍数,令
i=ik,j=jk
(即改变
i,j
表示的意义),得到
∑i=1⌊ak⌋∑j=1⌊bk⌋[(i,j)=1]
将布尔框用
μ
消去
∑i=1⌊ak⌋∑j=1⌊bk⌋∑t|(i,j)μ(t)
先枚举
t
于是先预处理出 μ ,然后线性算出答案。
代码
#include <bits/stdc++.h>
#define maxn 1000100
#define temp (i * prime[j])
#define LL long long
using namespace std;
int A, B, d, cnt;
LL ans;
int prime[maxn], miu[maxn];
bool vis[maxn];
void Da(){
miu[1] = 1;
for(int i = 2; i <= A; i++){
if(!vis[i]){
prime[++cnt] = i;
miu[i] = -1;
}
for(int j = 1; j <= cnt && temp <= A; j++){
vis[temp] = true;
if(i % prime[j] == 0){
miu[temp] = 0;
break;
}
else miu[temp] = -miu[i];
}
}
}
int main(){
scanf("%d%d%d", &A, &B, &d);
if(A > B) swap(A, B);
A /= d; B /= d;
Da();
for(int i = 1; i <= A; i++)
ans += 1LL * miu[i] * (A / i) * (B / i);
printf("%lld\n", ans);
return 0;
}