传送门
题目描述:
设 d ( x ) d(x) d(x) 为 x x x 的约数个数,给定 n n n、 m m m,求 ∑ i = 1 n ∑ j = 1 m d ( i j ) \sum\limits^n_{i=1}\sum\limits^m_{j=1}d(ij) i=1∑nj=1∑md(ij)。
输入格式:
输入文件包含多组测试数据。第一行,一个整数 T T T,表示测试数据的组数。接下来的 T T T 行,每行两个整数 n n n、 m m m。
输出格式:
T T T 行,每行一个整数,表示你所求的答案。
样例数据:
输入
2
7 4
5 6
输出
110
121
说明:
对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 50000 1\le n, m\le50000 1≤n,m≤50000, 1 ≤ T ≤ 50000 1\le T\le50000 1≤T≤50000
首先有一个结论,就是
d ( i j ) = ∑ x ∣ i ∑ y ∣ j [ gcd ( x , y ) = 1 ] d(ij)=\sum_{x|i}\sum_{y|j}[\;\gcd(x,y)=1\;] d(ij)=x∣i∑y∣j∑[gcd(x,y)=1]
证明的话我不是很会,先就当成结论用吧。
也就是说,我们相当于是求
∑ i = 1 n ∑ j = 1 m ∑ x ∣ i ∑ y ∣ j [ gcd ( x , y ) = 1 ] \sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[\;\gcd(x,y)=1\;] i=1∑nj=1∑mx∣i∑y∣j∑[gcd(x,y)=1]
把枚举因数换到前面去,即
∑ x = 1 n ∑ y = 1 m ⌊ n x ⌋ ⌊ m y ⌋ [ gcd ( x , y ) = 1 ] \sum_{x=1}^n\sum_{y=1}^m\lfloor\frac{n}{x}\rfloor\lfloor\frac{m}{y}\rfloor[\;\gcd(x,y)=1\;] x=1∑ny=1∑m⌊xn⌋⌊ym⌋[gcd(x,y)=1]
现在定义
f ( x ) = ∑ i = 1 n ∑ j = 1 m ⌊ n i ⌋ ⌊ m j ⌋ [ gcd ( i , j ) = x ] 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)=i=1∑nj=1∑m⌊in⌋⌊jm⌋[gcd(i,j)=x]
F ( x ) = ∑ x ∣ d f ( d ) F(x)=\sum_{x|d}f(d) F(x)=x∣d∑f(d)
那么,容易得出
F ( x ) = ∑ i = 1 n ∑ j = 1 m ⌊ n i ⌋ ⌊ m j ⌋ [ x ∣ gcd ( i , j ) ] F(x)=\sum_{i=1}^n\sum_{j=1}^m\lfloor\frac{n}{i}\rfloor\lfloor\frac{m}{j}\rfloor[\;x|\gcd(i,j)\;] F(x)=i=1∑nj=1∑m⌊in⌋⌊jm⌋[x∣gcd(i,j)]
将 x x x 提出来,消除 gcd ( i , j ) \gcd(i,j) gcd(i,j) 的影响
F ( x ) = ∑ i = 1 ⌊ n x ⌋ ∑ j = 1 ⌊ m x ⌋ ⌊ n i x ⌋ ⌊ m j x ⌋ F(x)=\sum_{i=1}^{\lfloor \frac{n}{x}\rfloor}\sum_{j=1}^{\lfloor \frac{m}{x}\rfloor}\lfloor\frac{n}{ix}\rfloor\lfloor\frac{m}{jx}\rfloor F(x)=i=1∑⌊xn⌋j=1∑⌊xm⌋⌊ixn⌋⌊jxm⌋
我们可以预处理出 S ( n ) = ∑ i = 1 n ⌊ n i ⌋ S(n)=\sum\limits_{i=1}^{n}\lfloor \frac{n}{i}\rfloor S(n)=i=1∑n⌊in⌋ 的值,那么 F ( x ) F(x) F(x) 就可以 O ( 1 ) O(1) O(1) 算了。
接下来就用莫比乌斯反演,得到
f ( x ) = ∑ x ∣ d m i n ( n , m ) F ( d ) μ ( d x ) f(x)=\sum_{x|d}^{min(n,m)}F(d)\mu(\frac d x) f(x)=x∣d∑min(n,m)F(d)μ(xd)
易知答案为 f ( 1 ) f(1) f(1),即
f ( 1 ) = ∑ d = 1 m i n ( n , m ) F ( d ) μ ( d ) f(1)=\sum_{d=1}^{min(n,m)}F(d)\mu(d) f(1)=d=1∑min(n,m)F(d)μ(d)
用整除分块计算就可以了。
#include
#include
#include
#define N 50005
#define ll long long
using namespace std;
int prime[N],mu[N],S[N];
bool mark[N];
void linear_sieves()
{
int i,j,sum=0;
memset(mark,true,sizeof(mark));
mark[0]=mark[1]=false,mu[1]=1;
for(i=2;i<N;++i)
{
if(mark[i]) prime[++sum]=i,mu[i]=-1;
for(j=1;j<=sum&&i*prime[j]<N;++j)
{
mark[i*prime[j]]=false;
if(i%prime[j]) mu[i*prime[j]]=-mu[i];
else break;
}
}
for(i=1;i<N;++i) mu[i]+=mu[i-1];
}
void prework()
{
int i,j,k;
for(k=1;k<N;++k)
{
int ans=0;
for(i=1;i<=k;i=j+1)
{
j=k/(k/i);
ans+=(k/i)*(j-i+1);
}
S[k]=ans;
}
}
ll solve(int n,int m)
{
int i,j;ll ans=0;
if(n>m) swap(n,m);
for(i=1;i<=n;i=j+1)
{
j=min(n/(n/i),m/(m/i));
ans+=1ll*(mu[j]-mu[i-1])*S[n/i]*S[m/i];
}
return ans;
}
int main()
{
int n,m,T;
scanf("%d",&T);
prework();
linear_sieves();
while(T--)
{
scanf("%d%d",&n,&m);
printf("%lld\n",solve(n,m));
}
return 0;
}