[SDOI2015]约数个数和
Description
设d(x)为x的约数个数,给定N、M,求
∑ni=1∑mj=1d(ij)
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
Solution
有一个结论:
∑ni=1∑mj=1d(ij)=∑ni=1∑mj=1⌊ni⌋⌊mj⌋[gcd(i,j)==1]
然后随便反演就行
【有谁能告诉我这结论是怎么想到的。。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MaxN = 50010;
int n,m;
LL miu[MaxN],d[MaxN],c[MaxN];
int prime[MaxN],flag[MaxN],cnt;
inline void kyuu(){
miu[1]=d[1]=1;
for(int i=2;i<=50000;i++){
if(!flag[i]) prime[++cnt]=i,miu[i]=-1,d[i]=2,c[i]=1;
for(int j=1;j<=cnt && prime[j]*i<=50000;j++){
flag[i*prime[j]]=1;
if(i%prime[j]==0){
miu[i*prime[j]]=0;
d[i*prime[j]]=d[i]/(c[i]+1)*(c[i]+2);
c[prime[j]*i]=c[i]+1;
break;
}
miu[i*prime[j]]=-miu[i];
d[i*prime[j]]=d[i]*2;
c[prime[j]*i]=1;
}
}
for(int i=2;i<=50000;i++) miu[i]+=miu[i-1],d[i]+=d[i-1];
}
int main()
{
int T=0;
kyuu();
for(scanf("%d",&T);T--;){
scanf("%d%d",&n,&m);
LL res=0;
for(int i=1,t;i<=n && i<=m;i=t+1)
t=min(n/(n/i),m/(m/i)),res+=(miu[t]-miu[i-1])*d[n/i]*d[m/i];
printf("%lld\n",res);
}
return 0;
}