洛谷P2257 YY的GCD 莫比乌斯函数反演+线性筛


洛谷P2257 YY的GCD


标签

  • 莫比乌斯反演
  • 线性筛

前言

  • 这题貌似和莫反没多大关系,就是用到了一个莫比乌斯函数的性质了,其他就是推公式,优化和式。
  • 我的第一道懵逼反演…真的好难好难…而且套路特别多,要多做。可能过段时间我就会觉得特别简单吧!
  • 这一条是我过了3天我再次回到这篇博客里编辑的,我想说,真的挺简单的,无非就是那些更换枚举项,反演替换,筛一下什么什么之类的了。觉得自己三天前太笨了,这么显然的东西都搞了半天…

简明题意

  • 给定n,m,求使得gcd(i,j)的值是素数,有多少对这样的ij

思路

  • 把题目写成公式形式,就是让你求:
    ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = = p ] ( p 是 所 有 质 数 ) \sum_{i=1}^n \sum_{j=1}^m[gcd(i,j)==p](p是所有质数) i=1nj=1m[gcd(i,j)==p](p)

    考虑到 g c d ( i , j ) gcd(i,j) gcd(i,j)的值不可能超过 m i n ( i , j ) min(i,j) min(i,j),因此p的取值应该是 m i n ( n , m ) min(n,m) min(n,m),也就是p是所有在 [ 1 , m i n ( n , m ) ] [1,min(n,m)] [1,min(n,m)]范围内的质数,而不是所有质数,但为了简单表述,下文中 “所有质数” 表示在 [ 1 , m i n ( n , m ) ] [1,min(n,m)] [1,min(n,m)]范围内的所有质数

  • 我们枚举任意质数,也就是把括号中的条件写到式子中,就是
    ∑ p : 所 有 质 数 ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = = p ] \sum_{p:所有质数}\sum_{i=1}^n \sum_{j=1}^m[gcd(i,j)==p] p:i=1nj=1m[gcd(i,j)==p]
  • 根据套路,是要把条件 [ g c d ( i , j ) = = p ] [gcd(i,j)==p] [gcd(i,j)==p]转换成 [ g c d ( i , j ) = = 1 ] [gcd(i,j)==1] [gcd(i,j)==1]的。所以我们套路一下,原式就变成:
    ∑ p : 所 有 质 数 ∑ i = 1 [ n p ] ∑ j = 1 [ m p ] [ g c d ( i , j ) = = 1 ] \sum_{p:所有质数}\sum_{i=1}^{[\frac np]} \sum_{j=1}^{[\frac mp]}[gcd(i,j)==1] p:i=1[pn]j=1[pm][gcd(i,j)==1]

为什么会有这样的套路呢?因为 g c d ( i , j ) = = 1 gcd(i,j)==1 gcd(i,j)==1这样的式子往往更好化简或干一些其他的事情,所以通常见到 g c d ( i , j ) = = p gcd(i,j)==p gcd(i,j)==p就更换枚举上界,使它变成 g c d ( i , j ) = = 1 gcd(i,j)==1 gcd(i,j)==1

  • 然后莫比乌斯函数有一个极其重要的结论
    ∑ d ∣ n μ ( d ) = [ n = = 1 ] \sum_{d|n} \mu(d)=[n==1] dnμ(d)=[n==1]

对于一个整数 n > 1 n>1 n>1,他的所有约数的莫比乌斯函数值之和,都是0,而1的所有约数莫比乌斯函数之和是1

  • 现在我们把这里的 n n n替换成 g c d ( i , j ) gcd(i,j) gcd(i,j),我们就得到了下面的式子
    ∑ d ∣ g c d ( i , j ) μ ( d ) = [ g c d ( i , j ) = = 1 ] \sum_{d|gcd(i,j)} \mu(d)=[gcd(i,j)==1] dgcd(i,j)μ(d)=[gcd(i,j)==1]
  • 然后我们把它代回原式,可以得:
    ∑ p : 所 有 质 数 ∑ i = 1 [ n p ] ∑ j = 1 [ m p ] ∑ d ∣ g c d ( i , j ) μ ( d ) \sum_{p:所有质数}\sum_{i=1}^{[\frac np]} \sum_{j=1}^{[\frac mp]} \sum_{d|gcd(i,j)} \mu(d) p:i=1[pn]j=1[pm]dgcd(i,j)μ(d)
  • 然后我们把 ∑ d ∣ g c d ( i , j ) μ ( d ) \sum_{d|gcd(i,j)} \mu(d) dgcd(i,j)μ(d)写到条件中去,去枚举每一个d,显然 g c d ( i , j ) < = m i n ( n , m ) gcd(i,j)<=min(n,m) gcd(i,j)<=min(n,m),也就是 d < = m i n ( n , m ) d<=min(n,m) d<=min(n,m),但我们现在的上下界不是nm,而是 [ n p ] , [ m p ] [\frac np],[\frac mp] [pn][pm]因此, d < = m i n ( [ n p ] , [ m p ] ) d<=min([\frac np],[\frac mp]) d<=min([pn],[pm]),原式变成:
    ∑ p : 所 有 质 数 ∑ d = 1 d < = m i n ( [ n p ] , [ m p ] ) ∑ i = 1 [ n p ] ∑ j = 1 [ m p ] ( μ ( d ) ∗ [ d ∣ g c d ( i , j ) ] ) \sum_{p:所有质数}\sum _{d=1}^{d<=min([\frac np],[\frac mp])} \sum_{i=1}^{[\frac np]} \sum_{j=1}^{[\frac mp]} \left(\mu(d) * [d|gcd(i,j)] \right) p:d=1d<=min([pn],[pm])i=1[pn]j=1[pm](μ(d)[dgcd(i,j)])

发现了吗,这一步实际上就是把 ∑ d ∣ g c d ( i , j ) μ ( d ) \sum_{d|gcd(i,j)} \mu(d) dgcd(i,j)μ(d),和式中的判断语句写到条件中

  • 接下来,我们可以移项,移动 μ ( d ) \mu(d) μ(d)那一项,然后就成了:
    ∑ p : 所 有 质 数 ∑ d = 1 d < = m i n ( [ n p ] , [ m p ] ) ( μ ( d ) ∗ ∑ i = 1 [ n p ] ∑ j = 1 [ m p ] [ d ∣ g c d ( i , j ) ] ) \sum_{p:所有质数}\sum _{d=1}^{d<=min([\frac np],[\frac mp])} \left( \mu(d) * \sum_{i=1}^{[\frac np]} \sum_{j=1}^{[\frac mp]}[d|gcd(i,j)] \right) p:d=1d<=min([pn],[pm])μ(d)i=1[pn]j=1[pm][dgcd(i,j)]

∑ i = 1 [ n p ] ∑ j = 1 [ m p ] μ ( d ) \sum\limits_{i=1}^{[\frac np]} \sum \limits_{j=1}^{[\frac mp]} \mu(d) i=1[pn]j=1[pm]μ(d)式子中,枚举项是i和j,与d无关,因此与d有关的项可以任意移动。也就是与枚举项无关的式子可以任意移项

  • 下一步,我们关注这个式子: ∑ i = 1 [ n p ] ∑ j = 1 [ m p ] [ d ∣ g c d ( i , j ) ] \sum\limits_{i=1}^{[\frac np]} \sum \limits_{j=1}^{[\frac mp]} [d |gcd(i,j)] i=1[pn]j=1[pm][dgcd(i,j)],实际上, d ∣ g c d ( i , j ) d|gcd(i,j) dgcd(i,j)等价于 [ d ∣ i 且 d ∣ j ] [d|i 且d|j] [didj],具体原因可以自行思考(但这个是经常用的,要熟知),所以这个式子就能变成:
    ∑ i = 1 [ n p ] ∑ j = 1 [ m p ] [ d ∣ i 且 d ∣ j ] \sum\limits_{i=1}^{[\frac np]} \sum \limits_{j=1}^{[\frac mp]} [d|i 且 d|j] i=1[pn]j=1[pm][didj]
  • 对于式子 ∑ i = 1 n [ d ∣ i ] \sum \limits_{i=1}^n[d|i] i=1n[di],表示d是i的约数的个数,显然d,2d,3d…这些是i的倍数,所以一共就有 [ n d ] [\frac nd] [dn]个,所以 ∑ i = 1 [ n p ] [ d ∣ i ] = [ n p d ] \sum \limits_{i=1}^{[\frac np]}[d|i]=[\frac n{pd}] i=1[pn][di]=[pdn],而现在需要d同时是ij的约数,因此答案就是 [ n p d ] ∗ [ m p d ] [\frac n{pd}]*[\frac m{pd}] [pdn][pdm],这样一来,我们和最后两个 ∑ ∑ \sum\sum 说拜拜,原式就成了
    ∑ p : 所 有 质 数 ∑ d = 1 d < = [ n p ] ( μ ( d ) ∗ [ n p d ] ∗ [ m p d ] ) \sum_{p:所有质数}\sum _{d=1}^{d<=[\frac np]} \left( \mu(d) * [\frac n{pd}]*[\frac m{pd}] \right) p:d=1d<=[pn](μ(d)[pdn][pdm])
  • 这时,复杂度大概是 O ( 质 数 个 数 ∗ [ n p ] ) O(质数个数*[\frac np]) O([pn]),最后可以拿到50分。接下来,我们考虑优化。首先我们有下面的式子:
    ∑ i = 1 n ∑ j = 1 [ n i ] f ( i j ) ∗ g ( i ) ∗ h ( j ) = ∑ k = 1 n f ( k ) ∗ ∑ d ∣ k g ( [ k d ] ) h ( d ) \sum_{i=1}^n\sum_{j=1}^{[\frac ni]}f(ij)*g(i)*h(j)=\sum_{k=1}^nf(k)*\sum_{d|k}g([\frac kd])h(d) i=1nj=1[in]f(ij)g(i)h(j)=k=1nf(k)dkg([dk])h(d)

k = i j k=ij k=ij,然后枚举 k k k。这里的 i i i可以不从1枚举到n,可以是任意数,前提是保证后面的d和i性质一样(比如i是[1,n]范围内的质数,那么d|k且d是质数),且计算时保证关于i的函数和关于d的函数是同一性质(还是刚才的例子,式子左边是g(i),右边以d作为自变量的也应该是g而不应该是h,但如果i是顺序从1枚举到n则没有这个限制)

  • 所以我们可以令 k = p d k=pd k=pd把原始改为枚举 k k k的形式。根据上面所说的, μ ( ) \mu() μ()的自变量就应该是 μ ( k d ) \mu(\frac kd) μ(dk)而不是 μ ( d ) \mu(d) μ(d)(其他题解都说要改为枚举k,但都没有说为什么,上面就是原因)
    ∑ k = 1 n [ n k ] [ m k ] ∗ ∑ d ∣ k 且 d ∈ p r i m e μ ( k d ) \sum_{k=1}^n[\frac nk][\frac mk]*\sum_{d|k且d \in prime}\mu(\frac kd) k=1n[kn][km]dkdprimeμ(dk)
  • 如果直接计算这个式子,会发现 ∑ d ∣ k 且 d ∈ p r i m e μ ( k d ) \sum\limits_{d|k且d \in prime}\mu(\frac kd) dkdprimeμ(dk)的前缀和比较难处理。现在问题就在于处理下面式子的前缀和:
    f ( k ) = ∑ d ∣ k 且 d ∈ p r i m e μ ( k d ) f(k)=\sum\limits_{d|k且d \in prime}\mu(\frac kd) f(k)=dkdprimeμ(dk)
  • 显然又是贡献法枚举了,枚举所有的d(且d是质数),然后d会对所有d的倍数 f ( k d ) f(kd) f(kd)产生贡献,这样一来就把 f ( k ) f(k) f(k)处理完了,然后前缀和一下。预处理复杂度是 O ( 约 数 个 数 ∗ n p ) ( p 是 指 每 一 个 约 数 ) O(约数个数*\frac np)(p是指每一个约数) O(pn)(p),总复杂度就是 O ( 约 数 个 数 ∗ n p + T n ) O(约数个数*\frac np + T\sqrt n) O(pn+Tn ),这样能拿到100分,但是可以进一步优化。

这里有一个小注意事项,就是整除分块里面的l和r必须用int,用long long就会超时…



分鸽线(上面就已经做完了,下面是一点优化,个人认为挺重要的,需要掌握)不加下面的优化大概15s,加了下面的优化就是10s



f ( k ) = ∑ d ∣ k 且 d ∈ p r i m e μ ( k d ) f(k)=\sum\limits_{d|k且d \in prime}\mu(\frac kd) f(k)=dkdprimeμ(dk)

  • 上式我们枚举的是什么?是k的所有质因子。于是就把 f ( k ) f(k) f(k)换成唯一分解形式:
    f ( k ) = μ ( k p 1 ) + μ ( k p 2 ) + . . . + μ ( k p n ) f(k)=\mu (\frac k{p_1})+\mu (\frac k{p_2})+...+\mu (\frac k{p_n}) f(k)=μ(p1k)+μ(p2k)+...+μ(pnk)
  • 然后发现 f ( k ) f(k) f(k)显然可以线性筛鸭!然后我们就线性筛,最终复杂度就是 O ( n + T n ) O(n + T\sqrt n) O(n+Tn )
  • 最后,我讲一下这里的线性筛:
  • 就举个例子吧,假设现在枚举的数是k=30,分解质因数后是
    k = 2 1 ∗ 3 1 ∗ 5 1 k=2^1*3^1*5^1 k=213151
  • 那么 f ( 30 ) = μ ( 30 2 ) + μ ( 30 3 ) + μ ( 30 5 ) f(30)=\mu(\frac {30}2)+\mu(\frac {30}3)+\mu(\frac {30}5) f(30)=μ(230)+μ(330)+μ(530)
    1. i % p r i m e [ j ] = = 0 i\%prime[j]==0 i%prime[j]==0
      在这个例子里 p r i m e [ j ] prime[j] prime[j]只能等于2(不知道为什么只有2?因为线性筛只会枚举<= i的最小质因子 的质数…),我们就假设现在枚举的质数是2,然后分解质因数是:
      k = 2 2 ∗ 3 1 ∗ 5 1 k=2^2*3^1*5^1 k=223151
      现在去枚举k的质因子p,发现,除了2,对于任意的p, k p \frac kp pk一定含有平方因子,也就是除了质因子2的,其他质因子的贡献都变成了0,所以 i ∗ p r i m e [ j ] = μ ( i ) i * prime[j]=\mu(i) iprime[j]=μ(i)
    2. i % p r i m e [ j ] ! = 0 i\%prime[j]!=0 i%prime[j]!=0
      这样的话,相当于多加了一个质因子,所以我们只用加上这个质因子的贡献 μ ( k p r i m e [ j ] ) \mu(\frac k{prime[j]}) μ(prime[j]k)。如果你信了的话,就错啦。并不是只用加上这个质因子的贡献的,因为k变大了,其他的质因子的贡献也变化了。每一个的质因子的 μ ( k d ) \mu(\frac kd) μ(dk) k d \frac kd dk都扩大了 p r i m e [ j ] prime[j] prime[j]倍,所以根据莫比乌斯函数的性质,增加了一个原来没有的质因子,值应该取反。于是变成 f [ i ∗ p r i m e [ j ] ] = − f ( i ) f[i * prime[j]]=-f(i) f[iprime[j]]=f(i)…还没完,现在还新增了一个质数prime[j],他的贡献是 μ ( i ∗ p r i m e [ j ] p r i m e [ j ] ) \mu(\frac {i*prime[j]}{prime[j]}) μ(prime[j]iprime[j])也就是 μ [ i ] \mu[i] μ[i],所以 f [ i ∗ p r i m e [ j ] ] = − f ( i ) + μ [ i ] f[i * prime[j]]=-f(i)+\mu[i] f[iprime[j]]=f(i)+μ[i]
    3. i是质数
      这个就太简单了,显然质数只有一个约数就是自己,所有值就是 μ ( 1 ) = 1 \mu(1)=1 μ(1)=1
    • 完结~

注意事项

  • 就是整除分块里面的l和r必须用int,用long long就会超时…

总结

  • 遇到枚举项中出现诸如[gcd(i,j)==1]的条件式,可以考虑用 ∑ \sum
  • [ d ∣ g c d ( i , j ) ] 等 价 于 [ d ∣ i 且 d ∣ j ] [d|gcd(i,j)]等价于[d|i且d|j] [dgcd(i,j)][didj]
  • ∑ d ∣ n μ ( d ) = [ n = = 1 ] \sum \limits_{d|n} \mu(d)=[n==1] dnμ(d)=[n==1]这个结论极其重要。可以用这个等式替换条件式
  • 就是整除分块里面的l和r必须用int,用long long就会超时…
  • 下面这一个我认为是这篇题解里最有营养的
    ∑ i = 1 n ∑ j = 1 [ n i ] f ( i j ) ∗ g ( i ) ∗ h ( j ) = ∑ k = 1 n f ( k ) ∗ ∑ d ∣ k g ( [ k d ] ) h ( d ) \sum_{i=1}^n\sum_{j=1}^{[\frac ni]}f(ij)*g(i)*h(j)=\sum_{k=1}^nf(k)*\sum_{d|k}g([\frac kd])h(d) i=1nj=1[in]f(ij)g(i)h(j)=k=1nf(k)dkg([dk])h(d)

k = i j k=ij k=ij,然后枚举 k k k。这里的 i i i可以不从1枚举到n,可以是任意数,前提是保证后面的d和i性质一样(比如i是[1,n]范围内的质数,那么d|k且d是质数),且计算时保证关于i的函数和关于d的函数是同一性质(还是刚才的例子,式子左边是g(i),右边以d作为自变量的也应该是g而不应该是h,但如果i是顺序从1枚举到n则没有这个限制)

  • 见到 ∑ d ∣ n 且 d ∈ p r i m e \sum\limits_{d|n且d \in prime} dndprime,就是在枚举n的所有质因子,看到这样的式子脑子里一定要立马蹦出线性筛鸭!!!

AC代码

#include
#include
using namespace std;

const int maxn = 1e7 + 10;

bool no_prime[maxn];
int prime[maxn], mu[maxn], f[maxn], pre[maxn];
int shai(int n)
{
	int cnt = 0;
	mu[1] = 1;

	for (int i = 2; i <= n; i++)
	{
		if (!no_prime[i])
			prime[++cnt] = i, mu[i] = -1, f[i] = 1;

		for (int j = 1; j <= cnt && prime[j] * i <= n; j++)
		{
			no_prime[i * prime[j]] = 1;
			mu[i * prime[j]] = i % prime[j] == 0 ? 0 : -mu[i];
			f[i * prime[j]] = i % prime[j] == 0 ? mu[i] : mu[i] - f[i];
			if (i % prime[j] == 0) break;
		}
	}

	for (int i = 1; i <= n; i++)
		pre[i] = pre[i - 1] + f[i];

	return cnt;
}

long long cal(int n, int m)
{
	int  l = 1, r; long long ans = 0;
	while (l <= n)
	{
		r = min(n / (n / l), m / (m / l));
		ans += (1ll * n / l) * (m / l) * (long long)(pre[r] - pre[l - 1]);
		l = r + 1;
	}
	return ans;
}

int n, m;

void solve()
{
	shai(maxn - 10);

	int t;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d%d", &n, &m);
		if (n > m) swap(n, m);

		printf("%lld\n", cal(n, m));
	}
}

int main()
{
	solve();
	return 0;
}

你可能感兴趣的:(#,线性筛,#,莫比乌斯反演)