CF895B XK Segments 题解 二分
题目链接:https://codeforces.com/problemset/problem/895/B
题目大意
给你一个长度为 \(n\) 的数列 \(a_1, a_2, \ldots, a_n\)。求数列中存在多少个不同的下标对 \((i, j)\) 满足如下条件:
\(a_i \le a_j\) 并且恰好有 \(k\) 个整数 \(y\) 满足 \(a_i \le y \le a_j\) 且 \(y\) 能被 \(x\) 整除。
注:在本题中,若 \(i \neq j\),则下标对 \((i, j)\) 和下标对 \((j, i)\) 是不同的下标对。
解题思路
首先我们可以将 \(a_1 \sim a_n\) 从小到大排序。
很明显排序前后的下标对数量不会变化。
但是排序后和下标 \(i\) 构成满足条件的下标对 \((i, j)\) 的下标 \(j\) 将会在一个连续的范围内。
其次要注意,本题中数据处理的过程中可能会发生超出 int 范围的情况,所以干脆全部都开 long long。即:
#define int long long
然后我们就可以枚举每个下标 \(i\),判断 \(a_i\) 开始(即下标 \(i, i+1, i+2, \ldots, n\) 范围内)存在多少个下标 \(j\) 满足
\(a_i \le a_j\) 并且恰好有 \(k\) 个整数 \(y\) 满足 \(a_i \le y \le a_j\) 且 \(y\) 能被 \(x\) 整除。
这个条件,然后把数量加起来就可以了。
具体来说:
- 当 \(i = 1\) 时,判断 \([1, n]\) 范围内存在多少下标 \(j\) 和 \(i\) 构成的下标对 \((i, j)\) 满足上述条件;
- 当 \(i = 2\) 时,判断 \([1, n]\) 范围内存在多少下标 \(j\) 和 \(i\) 构成的下标对 \((i, j)\) 满足上述条件;
- ……
- 当 \(i = n\) 时,判断 \([1, n]\) 范围内存在多少下标 \(j\) 和 \(i\) 构成的下标对 \((i, j)\) 满足上述条件。
即对于每个下标 \(i\) 判断区间 \([1, n]\) 范围内存在多少个下标 \(j\) 满足条件。
在 \(a_i = a_j\) 时,\(j\) 甚至可能小于 \(i\)。
(注意 \(i\) 可以等于 \(j\),即下标对 \((i, i)\) 也可能是满足条件的)
而且我们会发现,对于每个下标 \(i\),满足条件的下标 \(j\) 是一个连续的区间,这很好理解:
设满足条件的 \(j\) 所在的区间为 \([l_i, r_i]\)(而一整个区间是 \([1, n]\)),则:
- 区间 \([1, l_i - 1]\) 范围内:\(x\) 的倍数 \(\lt k\) 个;
- 区间 \([l_i, r_i]\) 范围内:\(x\) 的倍数恰好 \(k\) 个;
- 区间 \([r_i + 1, n]\) 范围内:\(x\) 的倍数 \(\gt k\) 个。
所以我们现在要做的事情就是找 \(i\) 对应的最小值 \(l_i\) 和最大值 \(r_i\)。
有一种特殊情况,\(k = 0\)。
\(k = 0\) 时
此时若 \(a_i\) 是 \(x\) 的倍数,则无解。(因为此时至少有一个 \(a_i\) 是 \(x\) 的倍数)
若 \(a_i\) 不是 \(x\) 的倍数,则 \(l_i = a_i\),\(r_i = \lceil \frac{a_i}{x} \rceil \times x - 1\)。
\(k \gt 0\) 时
\(l_i = \lceil \frac{a_i}{x} \rceil \times x + (k - 1) \times x\)
\(r_i = l_i + x - 1\)
确定好 \(l_i\) 和 \(r_i\) 之后 —— 我们用 l
表示 \(l_i\),用 r
表示 \(r_i\),那么
lower_bound(a+i, a+n+1, l) - a
对应的就是下标 \(l_i\)。
upper_bound(a+i, a+n+1, r) - a - 1
对应的就是下标 \(r_i\)。
则 \([l_i, r_i]\) 范围内满足条件的下标一共有 \(r_i - l_i + 1\) 即:
upper_bound(a+i, a+n+1, r) - lower_bound(a+i, a+n+1, l)
个。
对于每个下标 \(i\),确定好 \(l_i\) 和 \(r_i\) 之后,对上式进行累加即可。
示例程序
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
#define int long long
int n, x, k, a[maxn], ans;
signed main() {
cin >> n >> x >> k;
for (int i = 1; i <= n; i++)
cin >> a[i];
sort(a+1, a+n+1);
for (int i = 1; i <= n; i++) {
int l, r;
if (k == 0) {
if (a[i] % x == 0)
continue;
l = a[i], r = (a[i] + x - 1) / x * x - 1;
}
else {
l = (a[i] + x - 1) / x * x + (k - 1) * x;
r = l + x - 1;
}
ans += upper_bound(a+1, a+n+1, r) - lower_bound(a+1, a+n+1, l);
}
cout << ans << endl;
return 0;
}