Codeforces Round #779 (Div. 2) D 388535

本文介绍了一种解决D2.388535(HardVersion)问题的方法,利用01字典树来判断序列是否为指定范围内的全排列。通过对原序列进行特定操作,可以高效地确定序列的有效性。

D2. 388535 (Hard Version)

在这里插入图片描述
这里直接上 D2D2D2 的做法吧。我们可以知道 l⊕xl\oplus xlx 一定在 aaa 中,所以对于 (a[i]⊕l)(a[i]\oplus l)(a[i]l) (1<=i<=r-l+1) ,总有一个是 xxx 。我们现在设 b[i]=(a[i]⊕l)b[i]=(a[i]\oplus l)b[i]=(a[i]l) ,那么我们怎么判断 b[i]b[i]b[i] 是不是 xxx 呢?首先我们要知道原序列是一个排列,所以不会有相同的数字,且当前序列是通过原序列异或上同一个数字得到的,所以当前序列也不会有相同的数字,所以当前的序列再异或上一个相同的数字也不可能有相同的数字。

那么想要知道 b[i]b[i]b[i] 是否可以作为 xxx ,我们只要知道:
min{b[i]⊕a[j]∣1≤j≤r−l+1}==l ? min\{b[i]\oplus a[j]\mid 1\leq j\leq r-l+1\} ==l\ ? min{b[i]a[j]1jrl+1}==l ?
max{b[i]⊕a[j]∣1≤j≤r−l+1}==r ? max\{b[i]\oplus a[j]\mid 1\leq j\leq r-l+1\}==r\ ? max{b[i]a[j]1jrl+1}==r ?
因为如果最小值为 lll ,最大值为 rrr ,且我们知道每个数都不同,那不自然就是 [l,r][l,r][l,r] 的一个排列了么。求上述最小值和最大值就是经典的 010101 字典树问题了。

#include<bits/stdc++.h>

using namespace std;

const int N = 5e5 + 10;

int a[N];
int tot, nxt[20*N][2];

void clear() {
    tot = 0;
    nxt[0][0] = nxt[0][1] = 0;
}

void insert(int x) {
    int rt = 0;
    for (int i = 20; i >= 0; --i) {
        int cur = (x >> i & 1);
        if (!nxt[rt][cur]) {
            nxt[rt][cur] = ++tot;
            nxt[tot][0] = nxt[tot][1] = 0;    
        }
        rt = nxt[rt][cur];
    }
}

int query_min(int x) {
    int rt = 0, ans = 0;
    for (int i = 20; i >= 0; -- i) {
        int cur = (x >> i & 1);
        int need = cur;
        if (!nxt[rt][need]) {
            ans = (ans << 1) | 1;
            need = !need;
        }
        else {
            ans = (ans << 1);
        }
        rt = nxt[rt][need];
    }
    return ans;
}

int query_max(int x) {
    int rt = 0, ans = 0;
    for (int i = 20; i >= 0; -- i) {
        int cur = (x >> i & 1);
        int need = (cur ^ 1);
        if (!nxt[rt][need]) {
            ans = (ans << 1);
            need = !need;
        }
        else {
            ans = (ans << 1) | 1;
        }
        rt = nxt[rt][need];
    }
    return ans;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    int T; scanf("%d", &T);
    int n;
    while(T--) {
        int l, r; scanf("%d%d", &l, &r);
        for (int i = l; i <= r; ++i) {
            scanf("%d", &a[i]);
            insert(a[i]);
        }
        int x;
        for (int i = l; i <= r; ++i) {
            if (query_min(a[i] ^ l) == l && query_max(a[i] ^ l) == r) {  
                x = (a[i] ^ l);
                break;
            }
        }
        clear();
        printf("%d\n", x);
        // for (int i = l; i <= r; ++i) {
        //     cout << (x ^ a[i]) << ' ';
        // }
        // cout << endl << endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值