题目描述

[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_02的约数个数,给定[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_分解质因数_03[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_分解质因数_04,求 [SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_05
[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_06

题目分析

首先很不显然的有这样一个结论:
[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_07

证明如下

将一个数唯一质因数分解为[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_08,其约数个数为[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_09

考虑一个质数[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_10[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_11的影响,假设[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_分解质因数_12分解质因数后有[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_13[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_10,[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_15分解质因数后有[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_16[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_10,则产生的贡献即为[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_18,接下来是关键的一步(反正我想不到XD)
[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_19

(此句可自行忽略:只有当[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_20时才会有值,这就相当于一个[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_21的矩形,只取了左下角的[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_22字型的一块)

根据乘法原理,有
[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_分解质因数_23
因为必须满足所有[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_24才会对答案造成贡献,则可以变形为[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_25

所以[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_07

(以上证明摘自:传送门)

证毕

现在就有了[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_27

根据数论中切换枚举次序的套路以及莫比乌斯反演对布尔条件框的替换,我们得到
[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_分解质因数_28

于是这里使用分块优化,预处理[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_分解质因数_29的前缀和

最后考虑后面的两个Sigma怎么处理
可以观察发现它们都是[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_分解质因数_30的形式,此处可以用分块优化[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_31预处理

其实还可以[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_32预处理,可以发现[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_分解质因数_33
显然是约数个数和[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_34的前缀和函数,于是线性筛出[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_35[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_34,求出前缀和即可

每次询问[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_37, 预处理[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_32,总时间复杂度为[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_31

TIPS

线性筛约数个数时存一下最小的质数[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_40的次方[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_41,即存下[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_分解质因数_42就可以方便的线性筛了

线性筛约数和同理,存一下最小的质数[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_40所对应的首项为[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_分解质因数_44,公比为[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_40,项数为[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_质因数分解_46的等比数列,即存下[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)_约数个数_47就可以愉快的线性筛了

AC code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN = 100001;
namespace Mobius
{
	#define LL long long
	int Prime[MAXN], Cnt, mu[MAXN], d[MAXN], Minseq[MAXN];
	bool IsnotPrime[MAXN];
	LL sum_MU[MAXN], sum_D[MAXN];
	void init()
	{
		mu[1] = d[1] = Minseq[1] = 1;
		for(int i = 2; i < MAXN; ++i)
		{
			if(!IsnotPrime[i])
				Prime[++Cnt] = i, mu[i] = -1, d[i] = Minseq[i] = 2;
			for(int j = 1; j <= Cnt && i * Prime[j] < MAXN; ++j)
			{
				IsnotPrime[i * Prime[j]] = 1;
				if(i % Prime[j] == 0)
				{
					Minseq[i * Prime[j]] = Minseq[i]+1;
					mu[i * Prime[j]] = 0;
					d[i * Prime[j]] = d[i] / Minseq[i] * Minseq[i * Prime[j]];
					break;
				}
				Minseq[i * Prime[j]] = 2;
				mu[i * Prime[j]] = -mu[i];
				d[i * Prime[j]] = d[i]<<1;
			}
		}
		for(int i = 1; i < MAXN; ++i)
			sum_MU[i] = sum_MU[i-1] + mu[i],
			sum_D[i] = sum_D[i-1] + d[i];
	}
	LL calc(int N, int M)
	{
		if(N > M) swap(N, M);
		LL ret = 0;
		for(int i = 1, j; i <= N; i=j+1)
		{
			j = min(N/(N/i), M/(M/i));
			ret += (sum_MU[j]-sum_MU[i-1]) * sum_D[N/i] * sum_D[M/i];
		}
		return ret;
	}
}
using namespace Mobius;
int main ()
{
	init();
	int T, n, m;
	scanf("%d", &T);
	while(T--)
	{
		scanf("%d%d", &n, &m);
		printf("%lld\n", calc(n, m));
	}
}