CF1796C C. Maximum Set 題解 排列組合

quanjun發表於2024-09-04

題目連結: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;
}

相關文章