【SDOI 2015】约数个数和

【题目】

传送门

题目描述:

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=1nj=1md(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 1n,m50000 1 ≤ T ≤ 50000 1\le T\le50000 1T50000


【分析】

首先有一个结论,就是

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)=xiyj[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=1nj=1mxiyj[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=1ny=1mxnym[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=1nj=1minjm[gcd(i,j)=x]

F ( x ) = ∑ x ∣ d f ( d ) F(x)=\sum_{x|d}f(d) F(x)=xdf(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=1nj=1minjm[xgcd(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=1xnj=1xmixnjxm

我们可以预处理出 S ( n ) = ∑ i = 1 n ⌊ n i ⌋ S(n)=\sum\limits_{i=1}^{n}\lfloor \frac{n}{i}\rfloor S(n)=i=1nin 的值,那么 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)=xdmin(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=1min(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;
}

你可能感兴趣的:(#,莫比乌斯反演,#,整除分块)