2021“MINIEYE杯”中國大學生算法設計超級聯賽(1)1006.Xor sum


Xor sum

題目鏈接

http://acm.hdu.edu.cn/showproblem.php?pid=6955

題意

給定 \(n\) 個數,求異或和大於等於 \(k\) 的最短的區間左右端點,如果有多個答案,輸出左端點編號最小的那個。

思路

由於異或的自反性,我們做個前綴異或和可將區間異或和轉換成倆個數的異或和。

對於每一個 \(a_i\) ,要找到以 \(a_i\) 作為結尾的子串,只要和 \(i\) 之前的所有的異或前綴和做一次異或運算,就能得到以 \(i\) 為結尾的異或前綴和。

我們把前 \(i - 1\) 項結尾的異或前綴和用字典樹進行拆位儲存, 從高位往低位枚舉, 類似數位dp, 將大於的答案納入考慮范圍,不斷縮小左端點, 得到以 \(a_i\) 結尾的最短的目標區間。

AC_Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e7 + 50;
ll a[maxn];
int tot = 0;
int tree[maxn][2];
int cnt[maxn];
void insert(int x, int pos){
    int rt = 0;
    for(int i = 31;i >= 0;i--){
        int now = x >> i & 1;
        if(!tree[rt][now]){
            tree[rt][now] = ++tot;
        }
        rt = tree[rt][now];
        cnt[rt] = max(cnt[rt], pos);
    }
}
void find(int x, int k, int &L){
    int rt = 0;
    for(int i = 31;i >= 0;i--){
        int nowx = x >> i & 1;
        int nowk = k >> i & 1;
        if(!nowx){
            if(!nowk){
                L = max(L, cnt[tree[rt][1]]);
                if(!tree[rt][0]) return;
                rt = tree[rt][0];
            }
            else{
                if(!tree[rt][1]) return;
                rt = tree[rt][1];
            }
        }
        else{
            if(!nowk){
                L = max(L, cnt[tree[rt][0]]);
                if(!tree[rt][1]) return;
                rt = tree[rt][1];
            }
            else{
                if(!tree[rt][0]) return;
                rt = tree[rt][0];
            }

        }
    }
    L = max(L, cnt[rt]);
}
int main()
{
    std::ios::sync_with_stdio(false);
    int t;
    cin >> t;
    while(t--){
        int n, k;
        cin >> n >> k;
        for(int i = 1;i <= n;i++){
            cin >> a[i];
            a[i] ^= a[i - 1];
        }
        ll len = n + 1;
        ll ansL = 0;
        for(int i = 1;i <= n;i++){
            if(a[i] >= k){
                ansL = i;
                len = i;
                break;
            }
        }
        insert(0, 0);
        for(int i = 1; i <= n;i++){
            int L = 0;
            find(a[i], k, L);
            if(i - L < len && L){
                len = i - L;
                ansL = L + 1;
            }
            insert(a[i], i);
        }
        if(len != n + 1) cout << ansL << " " << ansL + len - 1 << endl;
        else cout << -1 << endl;
        for(int i = 0;i <= tot;i++) tree[i][0] = tree[i][1] = 0, cnt[i] = 0;
        tot = 0;
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM