bzoj3261 最大异或和

bzoj3261 题 目 传 送 门 b z o j 3261


题目

3261: 3261 : 最 大 异 或 和

TimeLimit:10Sec T i m e L i m i t : 10 S e c
MemoryLimit:512MB M e m o r y L i m i t : 512 M B

Description D e s c r i p t i o n

给定一个非负整数序列 a a ,初始长度为 N N
M个操作,有以下两种操作类型:
1 1 A x x :添加操作,表示在序列末尾添加一个数x,序列的长度 N+1 N + 1
2 2 Q l l r x x :询问操作,你需要找到一个位置p,满足 lpr l ≤ p ≤ r ,使得:
a[p] a [ p ] xor x o r a[p+1] a [ p + 1 ] xor x o r ... . . . xor x o r a[N] a [ N ] xor x o r x x 最大,输出最大是多少。

Input

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

Output

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

SampleInput S a m p l e I n p u t

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

SampleOutput S a m p l e O u t p u t

4
5
6

对于测试点 12NM5 1 − 2 , N , M ≤ 5
对于测试点 37NM80000 3 − 7 , N , M ≤ 80000
对于测试点 810NM300000 8 − 10 , N , M ≤ 300000
其中测试点 13579 1 , 3 , 5 , 7 , 9 保证没有修改操作。

0a[i]107 0 ≤ a [ i ] ≤ 10 7


题解

看到 xor x o r ,本能反应就是按位 xor x o r
首先用最裸的做法,用前缀和处理一下。
s[i] s [ i ] = = a[1] xor x o r a[2] a [ 2 ] xor x o r ... . . . xor x o r a[i] a [ i ]
a[p] a [ p ] xor x o r a[p+1] a [ p + 1 ] xor x o r ... . . . xor x o r a[N] a [ N ] xor x o r x x
= s[p1] s [ p − 1 ] xor x o r s[N] s [ N ] xor x o r x x
添加O(1),询问 O(N) O ( N ) ,时间 O(MN) O ( M N ) ,空间 O(N) O ( N )
观察 N N 的取值范围,可以想到用Trie
构造一棵可持久化 Trie T r i e ,询问时询问 s[l1] s [ l − 1 ] s[r1] s [ r − 1 ] s[N] s [ N ] xor x o r x x
因为高位的1比低位的 1 1 加起来还要大,所以高位的1先取。
添加 O(logN) O ( l o g N ) ,询问 O(logN) O ( l o g N ) ,时间 O(MlogN) O ( M l o g N ) ,空间 O(MlogN) O ( M l o g N )


总结

看到异或,本能反应就是位运算的方法。
例如:按位 xor x o r ,数位 DP D P Trie T r i e ,可持久化 Trie T r i e ……
这样的关于异或的题目,一般情况下,都可以用 O(logN) O ( l o g N ) 的时间解决……
简单地说,就是先想 Trie T r i e 。做不了,那我也没办法了……


标程

#include <bits/stdc++.h>
using namespace std;
const int N = 6e5 + 50;
struct tree{
    int c[2], cnt;
}t[N * 25];
int top, s[25], a[N], b[N], r[N];
int update(int y, int z)
{
    int ret = ++top, x = ret;
    for (int i = 23; i >= 0; i--)
    {
        t[x] = t[y]; t[x].cnt++;
        bool tmp = (z >> i) & 1;
        y = t[y].c[tmp];
        x = t[x].c[tmp] = ++top;
    }
    t[x] = t[y]; t[x].cnt++;
    return ret;
}
int query(int x, int y, int z)
{
    int ret = 0;
    for (int i = 23; i >= 0; i--)
    {
        bool tmp = (z >> i) & 1;
        if (t[t[x].c[tmp ^ 1]].cnt != t[t[y].c[tmp ^ 1]].cnt)
            {ret += s[i]; x = t[x].c[tmp ^ 1]; y = t[y].c[tmp ^ 1];}
        else {x = t[x].c[tmp]; y = t[y].c[tmp];}
    }
    return ret;
}
int main()
{
    int n, m; s[0] = 1;
    for (int i = 1; i <= 23; i++)
        s[i] = s[i - 1] << 1;
    scanf("%d%d", &n, &m); n++;
    r[1] = update(r[0], 0);
    for (int i = 2; i <= n; i++)
    {
        scanf("%d", &a[i]);
        b[i] = b[i - 1] ^ a[i];
        r[i] = update(r[i - 1], b[i]);
    }
    while (m--)
    {
        char c[10]; scanf("%s", c);
        if (c[0] == 'A')
        {
            scanf("%d", &a[++n]);
            b[n] = b[n - 1] ^ a[n];
            r[n] = update(r[n - 1], b[n]);
        }
        else
        {
            int x, y, z; scanf("%d%d%d", &x, &y, &z);
            printf("%d\n", query(r[x - 1], r[y], b[n] ^ z));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值