洛谷P2257 莫比乌斯反演+线性筛

题意:

给出 n , m n,m n,m,计算有多少 i ∈ [ 1 , n ] , j ∈ [ 1 , m ] i\in[1,n],j\in[1,m] i[1,n],j[1,m],使得 g c d ( i , j ) gcd(i,j) gcd(i,j)是一个素数

Solution:

不妨转化为枚举这个素数,找有多少对满足条件
∑ p ∈ p r i m e ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = p ] \sum_{p\in prime}\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=p] pprimei=1nj=1m[gcd(i,j)=p]
凑出莫比乌斯反演的形式,得到
∑ p ∈ p r i m e ∑ i = 1 ⌊ n p ⌋ ∑ j = 1 ⌊ m p ⌋ ∑ d ∣ g c d ( i , j ) μ ( d ) \sum_{p\in prime}\sum_{i=1}^{\lfloor\frac{n}{p}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{p}\rfloor}\sum_{d|gcd(i,j)}\mu(d) pprimei=1pnj=1pmdgcd(i,j)μ(d)
优先枚举因子,并且设 n ≤ m n\leq m nm
∑ p ∈ p r i m e ∑ d = 1 ⌊ n p ⌋ μ ( d ) ⌊ n p d ⌋ ⌊ m p d ⌋ \sum_{p\in prime}\sum_{d=1}^{\lfloor\frac{n}{p}\rfloor}\mu(d)\lfloor\frac{n}{pd}\rfloor\lfloor\frac{m}{pd}\rfloor pprimed=1pnμ(d)pdnpdm
优先枚举乘积,然后就可以将一部分转化为枚举因子,这里枚举 T = p d T=pd T=pd
∑ T = 1 n ⌊ n T ⌋ ⌊ m T ⌋ ∑ p ∈ p r i m e , p ∣ T μ ( T p ) \sum_{T=1}^{n}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum_{p\in prime,p|T} \mu(\frac{T}{p}) T=1nTnTmpprime,pTμ(pT)
前面的部分可以数论分块解决,现在只需要快速求得
f ( T ) = ∑ p ∈ p r i m e , p ∣ T μ ( T p ) f(T)=\sum_{p\in prime,p|T} \mu(\frac{T}{p}) f(T)=pprime,pTμ(pT)
可以线性筛筛出这个函数,线性筛关键在于添加一个质因子会带来什么后果,这里我们假设给 T T T加上一个质因子 p p p

有关莫比乌斯函数,不妨先假设 T T T的唯一分解为:
T = p 1 a 1 p 2 a 2 . . . p n a n T=p_{1}^{a_{1}}p_{2}^{a_{2}}...p_{n}^{a_{n}} T=p1a1p2a2...pnan

  • 如果 T T T包含了 p p p,那么 T p Tp Tp的唯一分解中 p p p的幂 ≥ 2 \geq2 2,而当 p ′ ≠ p p'\neq p p=p T p ′ \frac{T}{p'} pT必然包括平方因子 p 2 p^2 p2,此时所有的莫比乌斯函数值为 0 0 0,只有当 p ′ = p p'=p p=p时, T p ′ \frac{T}{p'} pT才有可能不出现平方因子,于是此时
    f ( T p ) = μ ( T ) f(Tp)=\mu(T) f(Tp)=μ(T)

  • 如果 T T T不包含 p p p,那么此时 T p Tp Tp的唯一分解为
    T p = p 1 a 1 p 2 a 2 . . . p n a n p Tp=p_{1}^{a_{1}}p_{2}^{a_{2}}...p_{n}^{a_{n}}p Tp=p1a1p2a2...pnanp

    枚举出所有的 T p ′ \frac{T}{p'} pT,有
    p 2 a 2 p 3 a 3 . . . p n a n p p 1 a 1 p 3 a 3 . . . p n a n p p 1 a 1 p 2 a 2 . . . p n a n p . . . p 1 a 1 p 2 a 2 . . . p n a n p_{2}^{a_{2}}p_{3}^{a_{3}}...p_{n}^{a_{n}}p\\ p_{1}^{a_{1}}p_{3}^{a_{3}}...p_{n}^{a_{n}}p\\ p_{1}^{a_{1}}p_{2}^{a_{2}}...p_{n}^{a_{n}}p\\ ...\\ p_{1}^{a_{1}}p_{2}^{a_{2}}...p_{n}^{a_{n}} p2a2p3a3...pnanpp1a1p3a3...pnanpp1a1p2a2...pnanp...p1a1p2a2...pnan
    除了最后一项都带有 p p p,而莫比乌斯函数是个积性函数,所以要求除了最后一项的莫比乌斯函数值,可以求得前面的部分,再乘 μ ( p ) \mu(p) μ(p),比如说第一项
    μ ( p 2 a 2 p 3 a 3 . . . p n a n p ) = μ ( p 2 a 2 p 3 a 3 . . . p n a n ) μ ( p ) \mu(p_{2}^{a_{2}}p_{3}^{a_{3}}...p_{n}^{a_{n}}p)=\mu(p_{2}^{a_{2}}p_{3}^{a_{3}}...p_{n}^{a_{n}})\mu(p) μ(p2a2p3a3...pnanp)=μ(p2a2p3a3...pnan)μ(p)
    此时我们将除最后一项的其他项都写成这样的形式,可以提出一个 μ ( p ) \mu(p) μ(p),此时这些的求和就为
    μ ( p ) [ μ ( p 2 a 2 p 3 a 3 . . . p n a n ) + μ ( p 1 a 1 p 3 a 3 . . . p n a n ) + μ ( p 1 a 1 p 2 a 2 . . . p n a n ) ] \mu(p)[\mu(p_{2}^{a_{2}}p_{3}^{a_{3}}...p_{n}^{a_{n}})+\mu(p_{1}^{a_{1}}p_{3}^{a_{3}}...p_{n}^{a_{n}})+\mu(p_{1}^{a_{1}}p_{2}^{a_{2}}...p_{n}^{a_{n}})] μ(p)[μ(p2a2p3a3...pnan)+μ(p1a1p3a3...pnan)+μ(p1a1p2a2...pnan)]
    中括号内的部分恰好就是 f ( T ) f(T) f(T),于是这部分值就为 μ ( p ) f ( T ) \mu(p)f(T) μ(p)f(T)

    最后一项即 T T T本身的莫比乌斯函数值,于是此时有
    f ( T p ) = μ ( p ) f ( T ) + μ ( T ) f(Tp)=\mu(p)f(T)+\mu(T) f(Tp)=μ(p)f(T)+μ(T)

最后在数论分块的时候需要区间求和 f ( T ) f(T) f(T),给 f ( T ) f(T) f(T)做一个前缀和即可 O ( 1 ) O(1) O(1)求出

// #include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using ll=long long;
const int N=1e7+5;

bool nt[N];
int prime[N],cnt,mu[N],f[N];

void make_prime()
{
	mu[1]=1;
	for(int i=2;i<=10000000;i++)
	{
		if(!nt[i]) prime[++cnt]=i,mu[i]=-1,f[i]=1;
		for(int j=1;j<=cnt&&i*prime[j]<=10000000;j++)
		{
			nt[i*prime[j]]=true;
			if(i%prime[j]==0)
			{
                mu[i*prime[j]]=0;
				f[i*prime[j]]=mu[i];
				break;
			}
			else
            {
                mu[i*prime[j]]=mu[i]*mu[prime[j]];
                f[i*prime[j]]=mu[i]+f[i]*mu[prime[j]];
            }
		}
	}
    for(int i=1;i<=10000000;i++) f[i]+=f[i-1];
}

int main()
{
	make_prime();
	int t; cin>>t;
	while(t--)
	{
		int n,m; ll ans=0; scanf("%d%d",&n,&m);
		if(n>m) swap(n,m);
		for(int l=1,r;l<=n;l=r+1)
		{
			r=min(n/(n/l),m/(m/l));
			ans+=1ll*(n/l)*(m/l)*(f[r]-f[l-1]);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

你可能感兴趣的:(算法)