BZOJ 3261 最大异或和 && qwb VS 去污棒(可持久化01Trie)

Problem I: qwb VS 去污棒 
Time Limit: 2 Sec Memory Limit: 256 MB 
Submit: 95 Solved: 36 
[Submit][Status][Web Board] 
Description

qwb表白学姐失败后,郁郁寡欢,整天坐在太阳底下赏月。在外人看来,他每天自言自语,其实他在和自己的影子“去污棒”聊天。 
去污棒和qwb互相出题考验对方,去污棒问了qwb这样一个问题: 
现已知一个有n个正整数的序列a[1],a[2]…a[n],接下来有m个操作

操作一共有两种:

1.在序列末尾添加一个数x。 
2.查询suf[p] xor x的最大值,其中xor是异或 ,l<=p<=r, 
suf[t]表示从t开始的后缀的异或和,即suf[t]=a[t] xor a[t+1] xor …xor a[len],len为序列长度。

Input

第一行一个整数T(<=5),表示一共有T组数据。

每组数据第一行两个整数n(<=200000),m(<=200000),意义如上所述。 
随后一行有n个数,表示初始序列。 
随后m行,每行表示一个操作。 
操作有两种,1: x 表示在末尾添加一个x,2: l r x表示查询suf[p] xor x的最大值,其中l<= p <= r, 
所有数及x不超过224 且保证所有操作合法。

Output

每组测试数据的第一行输出”Case x:”,x为数据组数的标号,从1开始。

接下来,对每个操作2输出一行答案。

Sample Input 

5 5 
1 2 3 4 5 
2 1 3 4 
1 10 
1 7 
2 4 4 5 
2 1 5 19

Sample Output 
Case 1: 


31

思路:

长度为n的序列有:prexor[n]^prexor[k] = sufxor[k+1]

因为是求异或最大 所以想到的是字典树,又因为询问的是区间,所以可以用可持久化字典树

具体思路见:点击打开链接

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e5+5;
int trie[maxn*25][2], sz[maxn*25], rt[maxn];
int n, m, cnt, sum;

int update(int pre, int i, int x)
{
    int now = ++cnt;
    if(!i)
    {
        trie[now][0] = trie[now][1] = 0;
        sz[now] = sz[pre]+1;
        return now;
    }
    int bt = ((x>>(i-1))&1);
    trie[now][1-bt] = trie[pre][1-bt];
    trie[now][bt] = update(trie[pre][bt], i-1, x);
    sz[now] = sz[trie[now][0]]+sz[trie[now][1]];
    return now;
}

int query(int l, int r, int i, int x)
{
    if(!i) return 0;
    int bt = ((x>>(i-1))&1);
    if(sz[trie[r][1-bt]]-sz[trie[l][1-bt]])
        return (1<<(i-1))+query(trie[l][1-bt], trie[r][1-bt], i-1, x);
    else
        return query(trie[l][bt], trie[r][bt], i-1, x);
}

int main(void)
{
    int _, ca = 1;
    cin >> _;
    while(_--)
    {
        scanf("%d%d", &n, &m);
        rt[0] = trie[0][0] = trie[0][1] = sz[0] = sum = 0;
        rt[0] = update(rt[0], 25, 0);
        int tmp;
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &tmp);
            sum ^= tmp;
            rt[i] = update(rt[i-1], 25, sum);
        }
        printf("Case %d:\n", ca++);
        while(m--)
        {
            int cmd, l, r, x;
            scanf("%d", &cmd);
            if(cmd == 1)
            {
                scanf("%d", &tmp);
                sum ^= tmp;
                n++;
                rt[n] = update(rt[n-1], 25, sum);
            }
            else
            {
                scanf("%d%d%d", &l, &r, &x);
                l--, r--;
                printf("%d\n", query(rt[l-1], rt[r], 25, sum^x));
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值