约数个数和
前言
越来越熟练
题目
讲解
原式 \(=\sum_{i=1}^n\sum_{j=1}^md(ij)\)
\(=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[gcd(x,y)=1]\) 这一步可谓是至关重要!
改写一下:\(\sum_{i=1}^n\sum_{j=1}^m\lfloor\frac{n}{i}\rfloor\lfloor\frac{m}{j}\rfloor[gcd(i,j)=1]\)
令 \(f(x)=\sum_{i=1}^n\sum_{j=1}^m\lfloor\frac{n}{i}\rfloor\lfloor\frac{m}{j}\rfloor[gcd(i,j)=x]\)
且 \(F(x)=\sum_{i=1}^n\sum_{j=1}^m\lfloor\frac{n}{i}\rfloor\lfloor\frac{m}{j}\rfloor[x|gcd(i,j)]\)
\(\Rightarrow F(x)=\sum_{i=1}^{\frac{n}{x}}\sum_{j=1}^\frac{m}{x}\lfloor\frac{n}{xi}\rfloor\lfloor\frac{m}{xj}\rfloor\)
\(\Rightarrow f(x)=\sum_{x|d}\mu(\frac{d}{x})F(d)\)
我们所求的即为 \(f(1)\)
\(f(1)=\sum_{d=1}^{\min(n,m)}\mu(d)\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^\frac{m}{d}\lfloor\frac{n}{di}\rfloor\lfloor\frac{m}{dj}\rfloor\)
\(\Rightarrow f(1)=\sum_{d=1}^{\min(n,m)}\mu(d)\sum_{i=1}^{\frac{n}{d}}\lfloor\frac{n}{di}\rfloor\sum_{j=1}^\frac{m}{d}\lfloor\frac{m}{dj}\rfloor\)
令 \(g(x)=\sum_{i=1}^x\lfloor\frac{x}{i}\rfloor\)
\(\Rightarrow f(1)=\sum_{d=1}^{\min(n,m)}\mu(d)g(\lfloor\frac{n}{d}\rfloor)g(\lfloor\frac{m}{d}\rfloor)\)
预处理 \(g(x)\) 即可
代码
int prime[MAXN],pn,s[MAXN],mu[MAXN],g[MAXN];
bool vis[MAXN];
void sieve(int x)
{
s[1] = mu[1] = 1;
for(int i = 2;i <= x;++ i)
{
if(!vis[i]) prime[++pn] = i,mu[i] = -1;
for(int j = 1;j <= pn && i * prime[j] <= x;++ j)
{
vis[i * prime[j]] = 1;
if(i % prime[j] ==0) break;
mu[i * prime[j]] = -mu[i];
}
s[i] = s[i-1] + mu[i];
}
for(int i = 1;i <= x;++ i)
{
LL ret = 0;
for(int l = 1,r;l <= i;l = r+1)
{
r = i / (i/l);
ret += 1ll * i / l * (r-l+1);
}
g[i] = ret;
}
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
sieve(50000);
for(int T = Read(); T ;-- T)
{
n = Read(); m = Read();
if(n > m) swap(n,m);
LL ans = 0;
for(int l = 1,r;l <= n;l = r+1)
{
r = Min(n/(n/l),m/(m/l));
ans += 1ll * (s[r] - s[l-1]) * g[n / l] * g[m / l];
}
Put(ans,'\n');
}
return 0;
}