洛谷 P2522 [HAOI2011]Problem b

题目链接

题意

\(n\)组询问,每次给出\(a,b,c,d,k\),求\(\sum\limits_{x=a}^{b}\sum\limits_{y=c}^{d}[\gcd(x,y)=k]\)

思路

容斥原理+数论分块+莫比乌斯反演

我做的莫比乌斯反演的第一道题= =

\(f(n,m)=\sum\limits_{i=1}^{n}\sum\limits_{j= 1}^{m}[\gcd(i,j)=k]\)

那么根据容斥原理,题目中的式子就转化成了\(f(b,d)-f(b, c - 1) - f(a - 1,d) + f(a - 1, c - 1)\)

所以我们接下来的问题就转化为了如何求\(f\)的值

现在来化简\(f\)的值

  1. 容易得出原式等价于$$\sum\limits_{i = 1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j = 1}^{\lfloor\frac{m}{k}\rfloor}[\gcd(i,j) = 1]$$

  2. 因为\(\epsilon(n) =\sum\limits_{d|n}\mu(d)=[n=1]\),由此可将原式化为

\[\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{k}\rfloor}\sum\limits_{d|gcd(i,j)}\mu(d) \]

  1. 改变枚举对象并改变枚举顺序,先枚举\(d\),得

\[\sum\limits_{d=1}^{\min(n,m)}\mu(d)\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}[d|i]\sum\limits_{j=1}^{\lfloor\frac{m}{k}\rfloor}[d|j] \]

也就是说当\(d|i\)\(d|j\)时,\(d|\gcd(i,j)\)

  1. 易得\(1\sim \lfloor\frac{n}{k}\rfloor\)中一共有\(\lfloor\frac{n}{dk}\rfloor\)\(d\)的倍数,同理\(1\sim \lfloor\frac{m}{k}\rfloor\)中一共有\(\lfloor\frac{m}{dk}\rfloor\)\(d\)的倍数,于是原式化为$$\sum\limits_{d=1}^{\min(n,m)}\mu(d)\lfloor\frac{n}{dk}\rfloor\lfloor\frac{m}{dk}\rfloor$$

此时已经可以\(O(n)\)求解,但是过不了,因为有很多值相同的区间,所以可以用数论分块来做

先预处理\(\mu\),再用数论分块,复杂度\(O(n+T\sqrt n)\)

我的代码每次得分玄学,看评测机心情,建议自己写

代码

/*
Author:loceaner
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 1e6 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int n, a, b, c, d, k, cnt, p[A], mu[A], sum[A];
bool vis[A];

void getmu() {
	int MAX = 50010;
	mu[1] = 1;
	for (int i = 2; i <= MAX; i++) {
		if (!vis[i]) mu[i] = -1, p[++cnt] = i;
		for (int j = 1; j <= cnt && i * p[j] <= MAX; j++) {
			vis[i * p[j]] = true;
			if (i % p[j] == 0) break;
			mu[i * p[j]] -= mu[i];
		}
	}
	for (int i = 1; i <= MAX; i++) sum[i] = sum[i - 1] + mu[i];
}

int work(int x, int y) {
	int ans = 0ll;
	int max = min(x, y);
	for (int l = 1, r; l <= max; l = r + 1) {
		r = min(x / (x / l), y / (y / l));
		ans += (1ll * x / (l * k)) * (1ll * y / (l * k)) * 1ll * (sum[r] - sum[l - 1]); 
	}
	return ans;
}

void solve() {
	a = read(), b = read(), c = read(), d = read(), k = read();
	cout << work(b, d) - work(a - 1, d) - work(b, c - 1) + work(a - 1, c - 1) << '\n';
}

signed main() {
	getmu();
	int T = read();
	while (T--) solve();
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/loceaner/p/12717450.html