bzoj 4659 Lcm 莫比乌斯反演

题面

题目传送门

解法

还是写得详细一点比较好

  • 我们可以比较显然地得出式子: i = 1 n j = 1 n μ ( g c d ( i , j ) ) 2 l c m ( i , j ) \sum_{i=1}^n\sum_{j=1}^n\mu(gcd(i,j))^2lcm(i,j)
  • 然后下面就是推导过程了:
    = d = 1 n μ ( d ) 2 d i = 1 n d j = 1 n d [ g c d ( i , j ) = = 1 ] i j =\sum_{d=1}^n\mu(d)^2d\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{n}{d}\rfloor}[gcd(i,j)==1]ij
  • 通过反演,我们可以得到下面的式子:
    = d = 1 n μ ( d ) 2 d d 1 n d μ ( d 1 ) d 1 2 i = 1 n d d 1 j = 1 n d d 1 i j =\sum_{d=1}^n\mu(d)^2d\sum_{d_1}^{\lfloor\frac{n}{d}\rfloor}\mu(d_1)d_1^2\sum_{i=1}^{\lfloor\frac{n}{dd_1}\rfloor}\sum_{j=1}^{\lfloor\frac{n}{dd_1}\rfloor}ij
  • 那么我们令 s ( n ) = i = 1 n i s(n)=\sum_{i=1}^n i ,就可以得到下面的式子:
    = d = 1 n μ ( d ) 2 d d 1 n d d 1 μ ( d 1 ) d 1 2 s ( n d d 1 ) 2 =\sum_{d=1}^n\mu(d)^2d\sum_{d_1}^{\lfloor\frac{n}{dd_1}\rfloor}\mu(d_1)d_1^2*s(\lfloor\frac{n}{dd_1}\rfloor)^2
  • 然后枚举 d d 1 = k dd_1=k ,可以得到:
    = k = 1 n s ( n k ) 2 d k μ ( d ) 2 d μ ( k d ) ( k d ) 2 =\sum_{k=1}^ns(\lfloor\frac{n}{k}\rfloor)^2\sum_{d|k}\mu(d)^2d*\mu(\frac{k}{d})(\frac{k}{d})^2
  • 我们令 f ( k ) = d k μ ( d ) 2 d μ ( k d ) ( k d ) 2 f(k)=\sum_{d|k}\mu(d)^2d*\mu(\frac{k}{d})(\frac{k}{d})^2 ,因为 n 4 1 0 6 n\leq 4*10^6 ,所以使用 O ( n log n ) O(n\log n) 的筛法是不能通过的。
  • 那么我们考虑如何线性筛这个函数 f ( k ) f(k) 。设 k = p 1 k 1 p m k m k=p_1^{k_1}\dots p_m^{k_m} ,显然,如果 k i > 2 \exist k_i>2 ,那么 f ( k ) = 0 f(k)=0
  • 可以发现, f f 是一个积性函数。那么我们只要处理出 f ( p k ) f(p^k) 即可
  • 因为 k 2 k\leq 2 ,所以我们对于 k = 1 / 2 k=1/2 分别讨论
  • 显然可以发现, f ( p ) = p 2 + p , f ( p 2 ) = p 3 f(p)=-p^2+p,f(p^2)=-p^3 ,所以我们是可以线性筛的。
  • 时间复杂度: O ( n + T n ) O(n+T\sqrt n)
  • 因为是对 2 30 2^{30} 取模,所以我们可以直接自然溢出,然后 & ( 2 30 1 ) \&(2^{30}-1) 即可。

代码

#include <bits/stdc++.h>
#define Mod ((1 << 30) - 1)
#define N 4000010
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
bool f[N]; int g[N], p[N], mx[N], mns[N], tmp[N];
int s(int n) {return n * (n + 1) / 2;}
void add(int &x, int y) {x += y;}
void sieve(int n) {
	memset(f, true, sizeof(f)); int len = 0; g[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (f[i]) {
			p[++len] = i, g[i] = i - 1ll * i * i;
			mns[i] = mx[i] = tmp[i] = 1;
		}
		for (int j = 1; j <= len && i * p[j] <= n; j++) {
			int k = i * p[j]; f[k] = false;
			if (i % p[j] == 0) {
				tmp[k] = tmp[i], mns[k] = mns[i] + 1;
				mx[k] = max(mx[tmp[k]], mns[k]);
				if (mx[k] > 2) {g[k] = 0; continue;}
				g[k] = g[tmp[k]] * (-p[j] * p[j] * p[j]);
				break;
			} else {
				tmp[k] = i, mns[k] = 1, mx[k] = max(mx[i], 1);
				if (mx[k] > 2) {g[k] = 0; continue;}
				g[k] = g[i] * (p[j] - p[j] * p[j]);
			}
		}
	}
	for (int i = 1; i <= n; i++) g[i] += g[i - 1];
}
int solve(int n, int m) {
	int ret = 0, x = 0;
	for (int i = 1; i <= n; i = x + 1) {
		x = min(n / (n / i), m / (m / i));
		add(ret, (g[x] - g[i - 1]) * s(n / i) * s(m / i));
	}
	return ret & Mod;
}
int main() {
	sieve(4e6); int T; read(T);
	while (T--) {
		int n, m; read(n), read(m);
		if (n > m) swap(n, m);
		cout << solve(n, m) << "\n";
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/84593521