Description
设d(x)为x的约数个数,给定N、M,求
Input
输入文件包含多组测试数据。
第一行,一个整数T,表示测试数据的组数。
接下来的T行,每行两个整数N、M。
Output
T行,每行一个整数,表示你所求的答案。
Sample Input
2
7 4
5 6
Sample Output
110
121
HINT
1<=N, M<=50000
1<=T<=50000
%%%ZZJ
%%%zmoiynlp
从这俩人的题解中分别吸取一些很神的地方自己写一份题解
首先考虑i* j的一个因数是由i的一个因数p和j的一个因数q相乘而得的,但是这里会有重复的情况,比如i=4,j=6的时候,因数4就由4*1 和 2*2累加两次。
解决方案是,让一个i和j的公共质因子优先乘到p上,等到i除去p已经没有这个质因子,在乘到q上,这样合法的方案一定有
(ip,j)==1
所以
∑ni=1∑mj=1d(i∗j)
=∑ni=1∑mj=1∑p|i∑q|j[(ip,q)==1]
=∑ni=1∑mj=1∑p|i∑q|j[(p,q)==1]
=∑np=1[np]∑mq=1[mq]∗[(p,q)==1]
=∑np=1[np]∑mq=1[mq]∗∑d|p,d|qμ(d)
=∑min(n,m)d=1μ(d)∑n/dp/d=1[np]∑m/dq/d=1[mq]
=∑min(n,m)d=1μ(d)∑ndp′=1[[nd]p′]∑mdq′=1[[md]q′]
由经验,我们可以发现
∑ni=1[ni]
是d函数的前缀和
证明:对于每一个i,n/i是1~n中整除i的数的个数
于是可以 线性筛法求出μ和d的前缀和,对d分块就解决了
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
#define maxn 50005
#define to i*prime[j]
#define long long long
int t,n,m;
int prime[maxn];
int p;
int miu[maxn];//used_as_prefix_sum_later
int d[maxn];
int tmp[maxn];
void sieve()
{
miu[1]=1;
d[1]=1;
for(int i=2;i<maxn;i++)
{
if(!tmp[i])
{
miu[i]=-1;
prime[++p]=i;
d[i]=2;
tmp[i]=2;
}
for(int j=1;j<=p&&to<maxn;j++)
{
if(i%prime[j]==0)
{
tmp[to]=tmp[i]+1;
d[to]=d[i]/tmp[i]*tmp[to];
miu[to]=0;
break;
}
else
{
tmp[to]=2;
d[to]=d[i]*tmp[to];
miu[to]=-miu[i];
}
}
miu[i]+=miu[i-1];
d[i]+=d[i-1];
}
}
int main()
{
//freopen(".in","r",stdin);
sieve();
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
long ans=0;
for(int i=1,p;i<=n&&i<=m;i=p+1)
{
p=min(m/(m/i),n/(n/i));
ans+=(long)(miu[p]-miu[i-1])*d[m/i]*d[n/i];
}
printf("%lld\n",ans);
}
}