约数个数和

前言

越来越熟练

题目

洛谷

讲解

原式 \(=\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;
}
posted @ 2021-01-13 17:29  皮皮刘  阅读(50)  评论(0编辑  收藏  举报