【BZOJ 2820】YY的GCD

【题目】

传送门

题目描述:

神犇 YY 虐完数论后给傻 × × kAc 出了一题:

给定 n , m n, m ,求 1 x n 1≤x≤n 1 y m 1≤y≤m gcd ( x , y ) \gcd(x, y) 为质数的 ( x , y ) (x, y) 有多少对。

kAc 这种傻 × × 必然不会了,于是向你来请教 ……

输入格式:

第一行一个整数 T T ,表示数据组数。

接下来 T T 行,每行两个正整数,表示 n , m n, m

输出格式:

T T 行,每行一个整数表示第 i i 组数据的结果

样例数据:

输入
2
10 10
100 100

输出
30
2791

提示:

对于 100 % 100\% 的数据, T 10000 T ≤ 10000 n , m 10000000 n, m ≤ 10000000


【分析】

题目要求的是这个东西:

i = 1 n j = 1 m [    gcd ( i , j )    i s    a    p r i m e    ] \sum_{i=1}^n\sum_{j=1}^m[\;\gcd(i,j)\;\mathrm {is\; a\; prime}\;]

如果用 P \mathbb{P} 表示素数集的话,上式就是

p P i = 1 n j = 1 m [    gcd ( i , j ) = p    ] \sum_{p\in \mathbb{P}}\sum_{i=1}^n\sum_{j=1}^m[\;\gcd(i,j)=p\;]

我们定义

f ( x ) = i = 1 n j = 1 m [    gcd ( i , j ) = x    ] f(x)=\sum_{i=1}^n\sum_{j=1}^m[\;\gcd(i,j)=x\;]

F ( x ) = x d f ( d ) F(x)=\sum_{x|d}f(d)

显然,我们可以得到

F ( x ) = i = 1 n j = 1 m [    x gcd ( i , j )    ] = n x m x F(x)=\sum_{i=1}^n\sum_{j=1}^m[\;x|\gcd(i,j)\;]=\lfloor \frac n x\rfloor\lfloor \frac m x\rfloor

用莫比乌斯反演得到

f ( x ) = x d μ ( d x ) F ( d ) f(x)=\sum_{x|d}\mu(\frac{d}{x})F(d)

那么,最后的答案就是

p P f ( p ) = p P p d μ ( d p ) n d m d \sum_{p\in\mathbb{P}}f(p)=\sum_{p\in\mathbb P}\sum_{p|d}\mu(\frac{d}{p})\lfloor \frac n d\rfloor\lfloor \frac m d\rfloor

我们把 d d 改为枚举 p p 的几倍,即

p P d = 1 m i n ( n , m ) p μ ( d ) n d p m d p \sum_{p\in\mathbb P}\sum_{d=1}^{\lfloor\frac{min(n,m)}{p}\rfloor}\mu(d)\lfloor \frac n {d\cdot p}\rfloor\lfloor \frac m {d\cdot p}\rfloor

D = d p D=d\cdot p ,交换一下枚举顺序,即

D = 1 m i n ( n , m ) p D , p P μ ( D p ) n D m D \sum_{D=1}^{min(n,m)}\sum_{p|D,p\in\mathbb P}\mu(\frac{D}{p})\lfloor \frac n {D}\rfloor\lfloor \frac m {D}\rfloor

继续化简,得到

D = 1 m i n ( n , m ) n D m D ( p D , p P μ ( D p ) ) \sum_{D=1}^{min(n,m)}\lfloor \frac n {D}\rfloor\lfloor \frac m {D}\rfloor(\sum_{p|D,p\in\mathbb P}\mu(\frac{D}{p}))

g ( D ) = p D , p P μ ( D p ) g(D)=\sum\limits_{p|D,p\in\mathbb P}\mu(\frac{D}{p})

那么,我们可以先线性筛出 μ \mu 的函数值,然后枚举每个质数以及每个质数的倍数把 g g 暴力更新出来。

剩下的就可以整除分块做了。


【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10000005
#define ll long long
using namespace std;
bool mark[N];
int prime[N],mu[N],g[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  {mu[i*prime[j]]=0;break;}
		}
	}
	for(i=1;i<=sum;++i)
	  for(j=1;j*prime[i]<N;++j)
	    g[j*prime[i]]+=mu[j];
	for(i=1;i<N;++i)  g[i]+=g[i-1];
}
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*(g[j]-g[i-1])*(n/i)*(m/i);
	}
	return ans;
}
int main()
{
	int n,m,T;
	scanf("%d",&T);
	linear_sieves();
	while(T--)
	{
		scanf("%d%d",&n,&m);
		printf("%lld\n",solve(n,m));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_dreams/article/details/87564861