Birthday Gift

最爱丁珰發表於2024-03-23

我們將\(x++\),從而最終的答案一定是要小於\(x\)的,也就一定要有一位不同

我們從高位到低位列舉最高的一位與\(x\)不同的位置\(i\)(也就是說,認為第\(i+1\)位到最高位都與\(x\)相同,但第\(i\)位不同)

我們先考慮更高位置如何相同

如果更高位置為\(0\),那麼那一位必須只能有偶數個\(1\)(否則無論怎麼分組,最終這一位都是\(1\)),為了讓組數最多,我們考慮每兩個相鄰\(1\)及其中間的\(0\)分為一組;我們從高到低地對每一位進行程式碼的分組,根據決策包容性,我們一定不會漏掉分組的組數。舉個例子說明

比如在列舉的時候,我們第一次列舉到一個\(0\),然後我們將每兩個相鄰\(1\)及其中間的\(0\)分為一組,如下

注意我們將每一組的所有數字都刪除掉了,並形成一個新組,新組的每一個數字是原來對應組所有數字的異或(可見程式碼)

接下來我們考慮更低位的時候,我們分組就要滿足上面的限制,比如如果我們要把四個\(1\)全部分在一起

就相當於合併新陣列的\(a_1,a_2\)以及中間的兩個\(0\),根據貪心的決策包容性,顯然沒有問題

如果更高位置為\(1\),那麼隨便怎麼分組都不會超過限制。只是有一個問題,隨便分組可能會導致這一位為\(0\)而不是\(1\),不滿足更高位都相同的要求。其實這個時候是不會遺漏最優解的,所以無所謂

主要是學習中間的決策包容性,以及不會遺漏最優解的操作

#include <bits/stdc++.h>
 
using namespace std;
 
using ll = long long;
 
#define int ll
#define all(a) a.begin(), a.end()
 
void solve() {
    int n, x;
    cin >> n >> x;
    ++x;
    vector<int> a(n);
    for (int &i: a)
        cin >> i;
    int res = -1;
    for (int i = 30; i >= 0; --i) {
        vector<int> b;
        bool open = false;//為1表示有奇數個1,為0表示有偶數個1 
        for (int j = 0; j < a.size(); ++j) {
            if (!open)
                b.push_back(a[j]);
            else
                b.back() ^= a[j];
            if (a[j] & (1 << i))
                open = !open;
        }//嘗試模擬一下這個迴圈,就是分組用的 
        if (!(x & (1 << i))) {//如果這一位是0 
            if (open) {//這一位有奇數個1
			//無論怎麼分組最後這一位都會為1,大於0
			//我們又認為更高位都相同,所以不用往下迴圈了 
                cout << res << '\n';
                return;
            }
            a = b;//替換掉合併分組後的陣列 
        } else {//如果這一位為1 
            if (!open)//這裡就是不會遺漏最優解的原因
			//可以想一下 
                res = max(res, (int) b.size());
        }
    }
    cout << res << '\n';
}
 
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
}