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;
}

posted @ 2021-07-20 23:08  Carered  阅读(225)  评论(11编辑  收藏  举报