ABC341D

2024-02-19

先想一个问题:哪些数满足恰好只被 $N$ 和 $M$ 中一个数整除?

很简单,是 $N$ 的倍数加上 $M$ 的倍数,再除去 $N$ 和 $M$ 的最小公倍数的倍数。

我们令 $N$ 和 $M$ 的最小公倍数为 $lcm$。那么 满足条件的这些数是以 $lcm$ 为循环节出现的。 证明很简单:如果在 $1 \sim lcm$ 这些数中,$a_1,a_2,\cdots,a_n$ 符合要求,那么 $a_i+k \times lcm$ 也一定符合要求。

我们设 $1 \sim lcm$ 中符合条件的数有 $s$ 个。那么每个 $k\times lcm+1 \sim (k+1) \times lcm$ 中符合条件的数就有 $s$ 个。我们要找第 $K$ 小的数,就要先锁定 $k$。

$k = \frac{(K-1)}{s}$,小学生数学题。这时就是要找 $k\times lcm+1 \sim (k+1) \times lcm$ 中第 $((K-1) \bmod s)+1$ 个满足条件的数了。我们先把 $k \times lcm$ 放进优先队列里,然后执行 $((K-1) \bmod s)+1$ 次如下操作:

  • 取出队首 $x$。如果 $x$ 是 $N$ 的倍数,把 $x+N$ 放进优先队列;如果 $x$ 是 $M$ 的倍数,把 $x+M$ 放进优先队列。

队首即为答案。

不懂可以配合代码食用:

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, m, K, ans;
priority_queue<int, vector<int>, greater<int> > q;
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cin >> n >> m >> K;
	int lcm = n * m / __gcd(n, m);
	int s = (lcm / n - 1) + (lcm / m - 1);
	int k = (K-1) / s;
	K = (K-1) % s + 1;
    q.push(k * lcm);
	while (K--) {
		int x = q.top(); q.pop();
		if (x % n == 0) q.push(x+n);
		if (x % m == 0) q.push(x+m);
	}
	cout << q.top();
	return 0;
}