题目描述
设d(x)为x的约数个数,给定N、M,求 i=1∑nj=1∑md(ij)
输入格式
输入文件包含多组测试数据。第一行,一个整数T,表示测试数据的组数。接下来的T行,每行两个整数N、M。
输出格式
T行,每行一个整数,表示你所求的答案。
输入输出样例
输入 #1
2
7 4
5 6
输出 #1
110
121
说明/提示
1<=N, M<=50000
1<=T<=50000
分析
首先有一个结论(别看我我不会证
d(ij)=x∣i∑y∣j∑[gcd(x,y)==1]
于是
ans=i=1∑nj=1∑md(ij)=i=1∑nj=1∑mx∣i∑y∣j∑[gcd(x,y)==1]=i=1∑nj=1∑mx∣i∑y∣j∑k∣x,k∣y∑μ(k)=k=1∑nμ(k)i=1∑⌊kn⌋⌊kdn⌋j=1∑⌊km⌋⌊kjm⌋
记 g(n)=i=1∑n⌊in⌋,于是
ans=k=1∑nμ(k)∗g(⌊kn⌋)∗g(⌊km⌋),除法分块即可。
总复杂度 O(nn +Tn )
代码如下
#include <bits/stdc++.h>
#define N 50005
#define LL long long
using namespace std;
int g[N], mu[N], s[N], x[N], p[N], cnt, maxn = 50000;
LL ans, z = 1;
int main(){
int i, j, n, m, t, l, r, T;
mu[1] = 1;
for(i = 2; i <= maxn; i++){
if(!x[i]) x[i] = 1, p[++cnt] = i, mu[i] = -1;
for(j = 1; j <= cnt; j++){
t = i * p[j];
if(t > maxn) break;
x[t] = 1;
if(i % p[j] == 0) break;
mu[t] = -mu[i];
}
}
for(i = 1; i <= maxn; i++) s[i] = s[i - 1] + mu[i];
for(i = 1; i <= maxn; i++){
for(l = 1; l <= i; l = r + 1){
r = i / (i / l);
g[i] += (r - l + 1) * (i / l);
}
}
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &m);
if(n > m) swap(n, m);
ans = 0;
for(l = 1; l <= n; l = r + 1){
r = min(n / (n / l), m / (m / l));
ans += z * (s[r] - s[l - 1]) * g[n / l] * g[m / l];
}
printf("%lld\n", ans);
}
return 0;
}