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;
}
posted @ 2024-08-13 21:53  quanjun  阅读(8)  评论(0编辑  收藏  举报