牛客练习赛72 D-brz的函数 (莫比乌斯反演)

题目链接

∑ i = 1 n ∑ j = 1 n μ ( i j ) \sum_{i=1}^{n}\sum_{j=1}^{n}μ(ij) i=1nj=1nμ(ij)

推公式:

由于积性函数的性质 当 g c d ( i , j ) = = 1 gcd(i,j) == 1 gcd(i,j)==1 的时候 μ ( i j ) = μ ( i ) ∗ μ ( j ) μ(ij) = μ(i)*μ(j) μ(ij)=μ(i)μ(j)

所以式子可以变成这样:

∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = = 1 ] μ ( i ) μ ( j ) \sum_{i=1}^{n}\sum_{j=1}^{n}[gcd(i,j)==1]μ(i)μ(j) i=1nj=1n[gcd(i,j)==1]μ(i)μ(j)

由反演性质可以将 [ g c d ( i , j ) = = 1 ] [gcd(i,j)==1] [gcd(i,j)==1] 转变为 ∑ k ∣ g c d ( i , j ) μ ( k ) \sum_{k|gcd(i,j)}μ(k) kgcd(i,j)μ(k) ,式子变成:

∑ i = 1 n ∑ j = 1 n ∑ k ∣ g c d ( i , j ) μ ( k ) μ ( i ) μ ( j ) \sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{k|gcd(i,j)}μ(k)μ(i)μ(j) i=1nj=1nkgcd(i,j)μ(k)μ(i)μ(j)

改变枚举顺序,将枚举k提前,可以得到:

∑ k = 1 n μ ( k ) ∑ i = 1 n k ∑ j = 1 n k μ ( i ∗ k ) μ ( j ∗ k ) \sum_{k=1}^{n}μ(k)\sum_{i=1}^{\frac{n}{k}}\sum_{j=1}^{\frac{n}{k}}μ(i*k)μ(j*k) k=1nμ(k)i=1knj=1knμ(ik)μ(jk)

再将每一项求和提出来可以变成:

∑ k = 1 n μ ( k ) ∑ i = 1 n k μ ( i ∗ k ) ∑ j = 1 n k μ ( j ∗ k ) \sum_{k=1}^{n}μ(k)\sum_{i=1}^{\frac{n}{k}}μ(i*k)\sum_{j=1}^{\frac{n}{k}}μ(j*k) k=1nμ(k)i=1knμ(ik)j=1knμ(jk)

然后发现后两项求的是同一个东西,再稍微做一点简化:

∑ k = 1 n μ ( k ) ( ∑ i = 1 n k μ ( i ∗ k ) ) 2 \sum_{k=1}^{n}μ(k)(\sum_{i=1}^{\frac{n}{k}}μ(i*k))^2 k=1nμ(k)(i=1knμ(ik))2

这样式子是推出来了,从后面一项求和的上界可以发现随着k的变化,总会有一段区间的值是不变的,这里就可以用整除分块来优化时间,把所有的答案 O ( n n ) O(n\sqrt{n}) O(nn ) 预处理出来, O ( 1 ) O(1) O(1) 输出就好了。

#include 
using namespace std;

const int MAX = 5e4;
bool check[MAX+10];
int prime[MAX+10];
int mu[MAX+10];
int pre[MAX+10], ans[MAX+10];

void Mobius() {
	memset(check,false,sizeof(check));
	mu[1] = 1;
	int tot = 0;
	for(int i = 2; i <= MAX; i++) {
		if( !check[i] ) {
			prime[tot++] = i;
			mu[i] = -1;
		}
		for(int j = 0; j < tot; j++) {
			if(i * prime[j] > MAX) break;
			check[i * prime[j]] = true;
			if( i % prime[j] == 0) {
				mu[i * prime[j]] = 0;
				break;
			} else {
				mu[i * prime[j]] = -mu[i];
			}
		}
	}
	
	for(int i = 1;i <= MAX; i++) {
		int res = 0;
		for(int l = i, r; l <= MAX; l = r+1) {
			r = min(MAX, l+i-1);
			res += mu[l];
			ans[l] += mu[i] * res * res;
			ans[r+1] += -mu[i] * res * res;
		}
	}
	
	for(int i = 1;i <= MAX; i++) {
		ans[i] = ans[i-1] + ans[i];
	}
}

int _, n;

int main() {
	Mobius();
	scanf("%d", &_);
	while(_--) {
		scanf("%d", &n);
		printf("%d\n", ans[n]);
	}
}

你可能感兴趣的:(莫比乌斯反演,gcd)