P3312 [SDOI2014]数表 [莫比乌斯反演,树状数组]

\(\sum_{i=1}^{n} \sum_{j=1}^{m} \sum_{d|gcd(i,j)} d\)

显然大家都会,跳过了(

然后我们在这里加一个条件。

\(\sum_{i=1}^{n} \sum_{j=1}^{m} \sum_{d|gcd(i,j)} d \times [\sum_{d|gcd(i,j)} d \leq a]\)

我们暂且忽略这个玩意,定义 \(sig_d = \sum_{k|d} k\)

然后直接枚举这个 \(d\),贡献是 \(\sum_{d=1}^{n} \sum_{i=1}^{\frac{n}{d}} \sum_{j=1}^{\frac{m}{d}} \times sig_d [gcd(i,j)==1]\)

反演常用套路,\(\sum_{d|n} mu_d = [n==1]\)

枚举一个 \(k\)

\(\sum_{d} sig_d \sum_{k=1}^{\frac{n}{d}} mu_k \times \frac{n}{kd} \times \frac{m}{kd}\)

然后我们发现可以令 \(T=kd\)
换个形式带入

\(sum_{T=1}^{n} \frac{n}{T} \times \frac{m}{T} \sum_{d|T} sig_d \times mu_{\frac{T}{d}}\)

然后我们发现只有 \(sig_d \leq a\) 的情况下才计入贡献,然后树状数组扫描线即可。

#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define pii pair<int, int>
using namespace std;
template <class T> inline void cmax(int&x, const T&y) { (x < y) && (x = y); }
template <class T> inline void cmin(int&x, const T&y) { (x > y) && (x = y); }

const int mod = 1ll << 31;
const int MAX = 1e5 + 1;
struct que { int n, m, a, id; };

inline void inc(int&x, int y) {
	x += y; if(x >= mod) x -= mod;
}

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

	auto getmu = [&](int n) {
		vector <int> mu(n, 0); mu[1] = 1;
		for(int i = 1; i < n; i++)
			for(int j = i * 2; j < n; j += i) mu[j] -= mu[i];
		return mu;
	};
	
	auto getsig = [&](int n) {
		vector <int> sig(n, 0);
		for(int i = 1; i < n; i++)
			for(int j = i; j < n; j += i) inc(sig[j], i);
		return sig;
	};
	
	vector <int> mu = getmu(MAX), sig = getsig(MAX), a(MAX);
	static int sg[(int)1e5 + 1];
	for(int i = 0; i < MAX; i++) sg[i] = sig[i], a[i] = i;
	sort(a.begin(), a.end(), [](int x, int y) { return sg[x] < sg[y]; });
	int t; cin >> t;
	vector <que> q; int cnt = 0;
	while(t--) {
		int n, m, a;
		cin >> n >> m >> a;
		if(n > m) swap(n, m);
		q.pb({n, m, a, cnt++});
	}
	sort(q.begin(), q.end(), [](que x, que y) { return x.a < y.a; });
	struct BIT {
		vector <int> c;
		int n;
		BIT(const int&_n) { n = _n + 1; c.resize(n + 1); }
		inline int low(int x) { return x & -x; }
		inline void add(int x, int y) { inc(y, mod); for(; x < n; x += low(x)) inc(c[x], y); }
		inline int qry(int x) { int ans = 0; for(; x; x ^= low(x)) inc(ans, c[x]); inc(ans, mod); return ans; }
	};
	BIT bit(MAX);
	
	auto ins = [&](int x) { for(int k = 1; x * k <= 1e5; k++) bit.add(x * k, mu[k] * sig[x] % mod); };
	auto qry = [&](int n, int m) {
		int ans = 0, las = 0, now = 0;
		for(int l = 1, r = 0; l <= n; l = r + 1) {
			r = min(n / (n / l), m / (m / l));
			now = bit.qry(r);
			inc(ans, (now - las + mod) % mod * (n / l) % mod * (m / l) % mod);
			las = now;
		}
		inc(ans, mod);
		return ans;
	};
	int j = 1;
	vector <int> ans(cnt, 0);
	for(int i = 0; i < cnt; i++) {
		while(j < MAX && sig[a[j]] <= q[i].a) ins(a[j++]);
		ans[q[i].id] = qry(q[i].n, q[i].m);
	}
	for(int i = 0; i < cnt; i++)
		cout << ans[i] << '\n';
	return 0;
}
posted @ 2020-05-26 13:05  _Isaunoya  阅读(156)  评论(1编辑  收藏  举报