题目链接:https://codeforces.com/problemset/problem/1796/C

题目大意:

定义一个集合 \(S\) 是合法的,当且仅当集合中任意两个整数 \(x\) 和 \(y\) 满足 \(x\) 被 \(y\) 整除或 \(y\) 被 \(x\)

有 \(t\) \((1\le t\le 2\times 10^4)\) 次询问,每次给你两个整数 \(l,r\) \((1\le l\le r \le 10^6)\),每次询问给出两个回答,第一个是在集合 \([l,r]\)

解题思路:

这个倍数关系肯定只有 \(2\) 倍或者 \(3\)

因为 \(4\) 倍关系(即如果有一个数 \(x\) 是另一个数 \(y\) 的四倍),因为 \(4 = 2 \times 2\),则我们其实可以再加入一个 \(2y\)。

其次 \(3\) 倍关系最多只有一个,因为如果有两个数 \(3\) 倍关系(\(3 \times 3 = 9 \gt 8 = 2 \times 2 \times 3\)),我们就可以得到三个数他们之间是 \(2\)

首先,对于一次查询的 \(l\) 和 \(r\),按照 \(l, 2l, 4l, 8l, \ldots\)

我们可以按照这个逻辑确定我们最多能取的数的个数,我们令这个数为 \(m+1\)。则此时最大的数恰好是最小的数的 \(2^m\),即恰好有 \(m\) 个 \(2\)

然后我们可以分析两种情况的集合数量。第一种是 \(m\) 个 \(2\) 倍关系,第二种是 \(m-1\) 个 \(2\) 倍关系 + \(1\) 个 \(3\)

\(m\) 个 \(2\)

设此时最小的数为 \(x\),则选择的 \(m+1\) 个数为:\(x, 2x, 4x, \ldots, 2^mx\)。

\(x\) 能取的最小的数值很明显是 \(l\),能取的最大的数字是 \(\lfloor \frac{r}{2^m} \rfloor\)

所以这一部分一共有 \(\lfloor \frac{r}{2^m} \rfloor - l + 1\)

\(m-1\) 个 \(2\) 倍关系 + \(1\) 个 \(3\)

同样,设此时最小的数为 \(x\),则只有 \(x \times 2^{m-1} \times 3 \le r\) 且 \(m \gt 0\)(因为要腾出一个 \(2\) 倍关系变为 \(3\)

此时 \(x\) 能取的最小的数仍然是 \(l\),能取的最大的数是 \(\lfloor \frac{r}{2^{m-1} \times 3} \rfloor\)

所以,最小值 \(x\) 有 \(\lfloor \frac{r}{2^{m-1} \times 3} \rfloor - l + 1\)

然后 \(3\) 倍关系放到 \(m\) 个倍数关系中,一共有 \(C_m^1 = m\) 种方案,最终的方案数还要乘上 \(m\)。

所以这一部分一共有 \(\lfloor \frac{r}{2^{m-1} \times 3} \rfloor - l + 1\)

特殊情况

要注意一种特殊情况,就是 \(m = 0\) 的情况,即最多只能选 \(m + 1 = 1\) 个数的情况,此时 \([l, r]\) 范围内任何一个数都可以选,选择方案数为 \(r - l + 1\)。

示例程序:

#include <bits/stdc++.h>
using namespace std;
int T, l, r;

void cal(int l, int r) {
    int m = log2(r/l);
    if (m == 0) { // 特判:只能选一个数的情况
        cout << 1 << " " << r - l + 1 << endl;
        return;
    }
    int ans = r / (1<<m) - l + 1; // 这一部分是m个2倍关系的方案数
    if ((l << m-1) * 3 <= r)
        ans += m * ((r >> m-1) / 3 - l + 1); // 这一部分加的是m-1个2倍关系+1个3倍关系的方案数
    cout << m+1 << " " << ans << endl;
}

int main() {
    cin >> T;
    while (T--) {
        cin >> l >> r;
        cal(l, r);
    }
    return 0;
}