P1829 [国家集训队]Crash的数字表格 / JZPTAB [莫比乌斯反演,狄利克雷前缀和]

\(\sum_{i=1}^{n}\sum_{j=1}^{m} lcm(i,j) = \frac{i,j}{\gcd(i,j)}\)

枚举 \(\gcd\)

\(\sum_{d=1}^{n}\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}ij\times d[\gcd(i,j)==1]\)

\(\sum_{i|n} \mu(i) = [n==1]\)

所以

\(\sum_{d=1}^{n}\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}ij\times d \sum_{k|gcd(i,j)} \mu(k)\)

枚举 \(k\)!
因为 \(k|\gcd(i,j)\),所以 \(k|i,k|j\)

\(\sum_{d=1}^{n}\sum_{k=1}^{n}\mu(k)\sum_{i=1}^{n/kd}\sum_{j=1}^{m/kd}ikjk\times d\)

发现 \(\sum_{i=1}^{x}\sum_{j=1}^{y}ij = sum2(x) \times sum2(y)\)
此处定义 \(sum2(x) = \frac{(1+x)\times x}{2}\)

然后原式就相当于

\(\sum_{d=1}^{n}d\sum_{k=1}^{n}k^2\mu(k)sum2(n/kd)\times sum2(m/kd)\)

发现 \(d\times k \leq n\)
枚举 \(d\times k\)

然后狄利克雷前缀和一下就没了。

// by Isaunoya
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int mod = 20101009;

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);

	auto add = [&](int&a, const int&b) {
		a += b;
		if(a >= mod)
			a -= mod;
	};
	
	auto init = [&](int n) {
		vector <int> mu(n + 1, 0);
		for(int i = mu[1] = 1; i <= n; i++)
			for(int j = i * 2; j <= n; j += i)
				mu[j] -= mu[i];
		
		vector <bool> vis(n + 1, 0);
		vector <int> prime;
		for(int i = 2; i <= n; i++) {
			if(vis[i])
				continue;
			prime.pb(i);
			for(int j = i * 2; j <= n; j += i)
				vis[j] = 1;
		}
		
		vector <int> sum(n + 1, 0);
		for(int i = 1; i <= n; i++) 
			sum[i] = (mu[i] * i + mod) % mod;
		for(int p: prime)
			for(int j = 1; j * p <= n; j++)
				add(sum[p * j], sum[j]);
		return sum;
	};
	
	int n, m;
	cin >> n >> m;
	if(n > m)
		swap(n, m);
	vector <int> s = init(n);
	
	auto get = [&](const int&x) {
		int res = 1ll * x * s[x] % mod;
		auto sum = [&](const int&x) { return 1ll * x * (x + 1) / 2 % mod; };
		res = 1ll * res * sum(n / x) % mod;
		res = 1ll * res * sum(m / x) % mod;
		return res;
	};
	
	int ans = 0;
	for(int i = 1; i <= n; i ++)
		add(ans, get(i));
	cout << ans << '\n';	
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/Isaunoya/p/12900107.html