题解:洛谷 P4735 最大异或和

【题目来源】

洛谷:P4735 最大异或和 - 洛谷

【题目描述】

给定一个非负整数序列 \(\{a\}\),初始长度为 \(N\)

\(M\) 个操作,有以下两种操作类型:

  1. A x:添加操作,表示在序列末尾添加一个数 \(x\),序列的长度 \(N\)\(1\)
  2. Q l r x:询问操作,你需要找到一个位置 \(p\),满足 \(l \le p \le r\),使得:\(a[p] \oplus a[p+1] \oplus ... \oplus a[N] \oplus x\) 最大,输出最大值。

【输入】

第一行包含两个整数 \(N, M\),含义如问题描述所示。
第二行包含 \(N\) 个非负整数,表示初始的序列 \(A\)
接下来 \(M\) 行,每行描述一个操作,格式如题面所述。

【输出】

假设询问操作有 \(T\) 个,则输出应该有 \(T\) 行,每行一个整数表示询问的答案。

【输入样例】

5 5
2 6 4 3 6
A 1 
Q 3 5 4 
A 4
Q 5 7 0 
Q 3 6 6 

【输出样例】

4
5
6

【算法标签】

《洛谷 P4735 最大异或和》 #可持久化# #前缀和# #字典树Trie# #O2优化#

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 600005;  // 数组最大容量
int n, m, idx, cnt, s;  // n,m: 输入参数,idx: 版本编号,cnt: 节点编号,s: 前缀异或和
int rt[N], ch[N * 25][2], siz[N * 25];  // rt: 每个版本的根节点,ch: Trie树左右儿子,siz: 子树大小

// 在可持久化Trie中插入一个值
void insert(int v)
{
    rt[++idx] = ++cnt;  // 新版本开点
    int x = rt[idx - 1];  // 旧版本根节点
    int y = rt[idx];      // 新版本根节点
    for (int i = 23; i >= 0; i--)  // 从高位到低位插入
    {
        int j = v >> i & 1;  // 获取第i位的值
        ch[y][!j] = ch[x][!j];  // 不同位继承旧版本
        ch[y][j] = ++cnt;  // 相同位新建节点
        x = ch[x][j];  // 旧版本走位
        y = ch[y][j];  // 新版本走位
        siz[y] = siz[x] + 1;  // 新节点子树大小增加1
    }
}

// 查询[l, r]区间内与v异或的最大值
int query(int x, int y, int v)
{
    int ans = 0;
    for (int i = 23; i >= 0; i--)
    {
        int j = v >> i & 1;  // 获取v的第i位
        if (siz[ch[y][!j]] > siz[ch[x][!j]])  // 如果存在可以使异或结果更大的分支
        {
            x = ch[x][!j];  // 旧版本走位
            y = ch[y][!j];  // 新版本走位
            ans += (1 << i);  // 累加结果
        }
        else  // 否则走另一条路
        {
            x = ch[x][j];
            y = ch[y][j];
        }
    }
    return ans;
}

int main()
{
    cin >> n >> m;  // 读入n和m
    
    insert(0);  // 插入初始值0作为边界
    
    // 读入初始序列并建立前缀异或和的版本
    for (int i = 1; i <= n; i++)
    {
        int x;
        cin >> x;
        s ^= x;  // 更新前缀异或和
        insert(s);  // 插入前缀异或和
    }
    
    // 处理m个操作
    while (m--)
    {
        char op;
        cin >> op;
        if (op == 'A')  // 添加操作
        {
            int x;
            cin >> x;
            s ^= x;  // 更新前缀异或和
            insert(s);  // 插入新版本
        }
        else  // 查询操作
        {
            int l, r, x;
            cin >> l >> r >> x;
            // 查询区间[l, r]内与s^x异或的最大值
            cout << query(rt[l - 1], rt[r], s ^ x) << endl;
        }
    }
    
    return 0;
}

【运行结果】

5 5
2 6 4 3 6
A 1
Q 3 5 4
4
A 4
Q 5 7 0 
5
Q 3 6 6 
6
posted @ 2026-02-19 14:29  团爸讲算法  阅读(3)  评论(0)    收藏  举报