题解[SDOI2015]约数个数和
首先可以得出 \(d(ij)=\sum\limits_{x|i}\sum\limits_{y|j}(gcd(x,y)=1)\)
证明
首先令 \(i=p_1^{\alpha_1}p_2^{\alpha_2}...p_k^{\alpha_k},j=p_1^{\beta_1}p_2^{\beta_2}...p_k^{\beta_k}\)
那么 \(ij=p_1^{\alpha_1+\beta_1}p_2^{\alpha_2+\beta_2}...p_k^{\alpha_k+\beta_k}\)
可以得出约数个数为 \((\alpha_1+\beta_2+1)(\alpha_2+\beta_2+1)...(\alpha_k+\beta_k+1)\)
由 \(x|i,y|j,gcd(x,y)=1\)
可知对于每一个 \(p_i\),
\(x,y\) 可以选 \((p_i^0,p_i^0),(p_i^1,p_i^0)...(p_i^{\alpha^i},p_i^0),(p_i^0,p_i^1)...(p_i^0,p_i^{\beta^i})\)
一共是 \(\alpha^i+\beta^i+1\) 种选择
那么加起来就等于约数个数了
证毕
接下来才是重头戏
令 \(F_k=\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{x|i}\sum\limits_{y|j}(k|gcd(x,y)),f_k=\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{x|i}\sum\limits_{y|j}(gcd(x,y)=k)\)
则 \(F_k=\sum\limits_{k|d}f_d\)
由莫比乌斯反演可以得出 \(f_k=\sum\limits_{n|d}\mu(\frac{d}{n})F_d\)
但是到这依然不好求,考虑优化一下 \(F_k\)
交换一下循环顺序
可以得出外面两层循环的范围,那么里面两层呢?可以看出最里面的值与 \(i,j\) 无关,所以只需要考虑 \((n,m)\) 有多少个 \((i,j)\) 满足里面的条件,得出
令 \(x`=\frac{x}{k},y`=\frac{y}{k},n`=\frac{n}{k},m`=\frac{m}{k}\),得
尝试将它展开,可以得到类似于 \(a_1 b_1+a_1 b_2+...+a_1 b_n+a_2 b_1+...+a_2 b_n+...\) 的形式,等于 \((a_1+a_2+...+a_n)(b_1+b_2+...+b_n)\)
那么 \(F_k=(\sum\limits_{x`=1}^{n`}\lfloor\frac{n`}{x`}\rfloor)(\sum\limits_{y`=1}^{m`}\lfloor\frac{m`}{y`}\rfloor)\)
不妨令 \(h(x)=\sum\limits_{i=1}^x\lfloor\frac{x}{i}\rfloor\)
再代回上面的式子
到这就很明显可以用整除分块预处理出来所有的 \(h(x)\) 和求答案了
代码
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 5e4 + 5;
typedef long long ll;
int prime[N], mu[N], sum[N], h[N], tot;
bool st[N];
int g(int k, int x)
{
return k / (k / x);
}
void init()
{
mu[1] = 1;
for (int i = 2; i < N; i++)
{
if (!st[i])
prime[++tot] = i, mu[i] = -1;
for (int j = 1; prime[j] * i < N; j++)
{
st[i * prime[j]] = true;
if (i % prime[j] == 0)
break;
mu[i * prime[j]] = -mu[i];
}
}
for (int i = 1; i < N; i++)
sum[i] = sum[i - 1] + mu[i];
for (int i = 1; i < N; i++)
{
for (int l = 1, r; l <= i; l = r + 1)
{
r = min(i, g(i, l));
h[i] += (r - l + 1) * (i / l);
}
}
}
int main()
{
init();
int T;
scanf("%d", &T);
while (T--)
{
int n, m;
scanf("%d%d", &n, &m);
int k = min(n, m);
ll ans = 0;
for (int l = 1, r; l <= k; l = r + 1)
{
r = min(k, min(g(n, l), g(m, l)));
ans += (ll)(sum[r] - sum[l - 1]) * h[n / l] * h[m / l];
}
printf("%lld\n", ans);
}
return 0;
}