Luogu P4735 最大異或和

題目鏈接:傳送門
支持在序列末尾添一個數,查詢區間l,rl,r內一個位置使這個位置到序列末尾和給定數x的異或值最大

可持久化01trie
就是每個節點存數字的個數
從高位到低位貪心
區間中一個位置到n的異或值與x異或最大
就是n的異或前綴和與x異或後的值與區間內任意前綴和異或最大
所以將n的異或前綴和與x異或的值放在01trie中貪心找反位就可以了
這樣一化簡
題面裏的式子就是s[p-1] ^ s[n] ^ x
s[]s[]是異或前綴和
沒有故意壓行
就刪了那麼倆回車
真的
直接這樣會T一個點
要不開O2要不加快讀

#include <cstdio>
#define A 15000010

int s[A], ch[A][2], cnt, rt[A], n, m, a, b, c, sum;
void build(int pre, int &k, int val, int pos) {
    k = ++cnt, s[k] = s[pre] + 1;
    if (pos < 0) return; int iw = (val >> pos) & 1;
    ch[k][iw ^ 1] = ch[pre][iw ^ 1];
    build(ch[pre][iw], ch[k][iw], val, pos - 1);
}
int ask(int pre, int k, int val, int pos) {
    if (pos < 0) return 0; int iw = (val >> pos) & 1;
    if (s[ch[k][iw ^ 1]] - s[ch[pre][iw ^ 1]] > 0) return (1 << pos) + ask(ch[pre][iw ^ 1], ch[k][iw ^ 1], val, pos - 1);
    else return ask(ch[pre][iw], ch[k][iw], val, pos - 1);
}

int main(int argc, char const *argv[]) {
    scanf("%d%d", &n, &m); build(rt[0], rt[1], 0, 24); n++;
    for (int i = 2; i <= n; i++) scanf("%d", &a), sum ^= a, build(rt[i - 1], rt[i], sum, 24);
    while (m--) {
        char opt[1]; scanf("%s", opt);
        if (opt[0] == 'A') scanf("%d", &a), sum ^= a, build(rt[n], rt[n + 1], sum, 24), n++;
        else scanf("%d%d%d", &a, &b, &c), printf("%d\n", ask(rt[a - 1], rt[b], c ^ sum, 24));
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章