P3327 [SDOI2015]约数个数和

Link

一句话题意

\(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{m} d(ij)\)

题解:

首先 \(d\) 函数有个性质, \(d(i,j) == \displaystyle\sum_{x \mid i}\sum_{y\mid j} [gcd(i,j) == 1]\)

然后我们要求的就是 :

\(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x\mid i}\sum_{y\mid j} [gcd[i,j] == 1]\)

直接上莫比乌斯反演,变成:

\(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x\mid i}\sum_{y \mid j} \sum_{p\mid x,p\mid y} \mu(p)\)

考虑到这么多的枚举量不太好算,所以我们需要减少一下枚举量。

先枚举一下 \(x\)\(y\) 试试,变成:

\(\displaystyle\sum_{x=1}^{n}\sum_{j=1}^{m}\sum_{x \mid i}\sum_{y\mid j}\sum_{p\mid x,p \mid y} \mu(p)\)

发现倒数第三个和倒数第四个其实求的是 \(1-n\)\(x\) 的倍数,以及 \(1-m\)\(y\) 的倍数。

就可以写成:

\(\displaystyle\sum_{x=1}^{n}\sum_{y=1}^{m}\lfloor {n\over x}\rfloor\lfloor{m \over j}\rfloor \sum_{p \mid x,p\mid j} \mu(p)\)

先枚举一下 \(p\) 试试,

\(\displaystyle\sum_{p=1}^{n}\mu(p) \sum_{x=1}^{n}[p\mid x]\sum_{j=1}^{m} [p\mid j] \lfloor {n\over x}\rfloor\lfloor{m\over j}\rfloor\)

中间那两个柿子还可以在化简一下变成:

\(\displaystyle\sum_{p=1}^{n}\mu(p)\sum_{x=1}^{n\over d}\sum_{y=1}^{y\over d} \lfloor{n\over px}\rfloor\lfloor{m\over py}\rfloor\)

把两个向下取整拆开变为:

\(\displaystyle\sum_{p=1}^{n}\mu(p)\sum_{x=1}^{n\over p} \lfloor{n\over px}\rfloor\sum_{j=1}^{m\over p}\lfloor{m\over py}\rfloor\)

\(g(n)\) 表示 \(\displaystyle\sum_{i=1}^{n} {n\over i}\)

那么上面的柿子可以写成 \(\displaystyle\sum_{p=1}^{n}\mu(p) g({n\over p}) g({m\over p})\)

对于 \(g\) 我们可以利用整除分块求出来,最后的柿子也要搞个整除分块,所以就是整除分块套整除分块(老千层饼了

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define LL long long
const int N = 50010;
int n,m,T,tot;
LL mu[N],sum[N],prime[N];
bool check[N];
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
	return s * w;
}
void YYCH()
{
	mu[1] = 1;
	for(int i = 2; i <= N-5; i++)
	{
		if(!check[i])
		{
			prime[++tot] = i;
			mu[i] = -1;
		}
		for(int j = 1; j <= tot && i * prime[j] <= N-5; j++)
		{
			check[i * prime[j]] = 1;
			if(i % prime[j] == 0)
			{
				mu[i * prime[j]] = 0;
				break;
			}
			else
			{
				mu[i * prime[j]] = -mu[i];
			}
		}
	}
	for(int i = 2; i <= N-5; i++) mu[i] += mu[i-1]; //求mu的前缀和
	for(int i = 1; i <= N-5; i++)
	{
		for(int l = 1, r; l <= i; l = r+1)//求g函数值
		{
			r = min(i,(i/(i/l)));
			sum[i] += (r-(l-1)) * (i/l); 
		}
	}
}
LL slove(int n,int m)
{
	LL res = 0;
	for(int l = 1, r; l <= n; l = r+1)//套个整除分块
	{
		r = min(n/(n/l),m/(m/l));
		res += (mu[r]-mu[l-1]) * sum[n/l] * sum[m/l];
	}
	return res;
}
int main()
{
	T = read(); YYCH();
	while(T--)
	{
		n = read(); m = read();
		if(n > m) swap(n,m);
		printf("%lld\n",slove(n,m));
	}
	return 0;
}
posted @ 2020-09-17 11:53  genshy  阅读(132)  评论(0)    收藏  举报