[BZOJ3261] 最大异或和

这题我借鉴了“主席树”的思想。

令v_i = x_1 ^ x_2 ^ ... ^ x_i 将v写成二进制,建立可持久化trie。

对于增加一个数,就相当于多开一个版本。

对于一个询问l,r,x,相当于求v_i ^ (v_N ^ x)最大(l - 1 <= i <= r - 1)。v_N ^ x是定值,于是贪心地找i就可以了。也就是对于一个v_N ^ x的二进制位是0则尽量在可持久化trie中找这位是1的。反之亦然。

实现的时候有个细节,就是可以在数列的最前面加一个0,这样方便处理。

时间复杂度O((N+M)*log 10^7) = O((N+M)*24) 空间复杂度大约为O((N+M)*24)

 (如果在IE浏览器下要拷代码的话复制到word里面就行了,虽然我也不知道为什么)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**************************************************************
     Problem: 3261
     User: lazycal
     Language: C++
     Result: Accepted
     Time:4436 ms
     Memory:174244 kb
****************************************************************/
 
#include <cstdio>
const int N = 600000 + 9;
int root[N],v[N],Vc,num,t[25];
struct Tree
{
     int lc,rc,size;
}tree[N*24];
inline void ten_to_two( int x, int (&t)[25])
{
     t[0] = 0;
     for (;x;x/=2) t[++t[0]] = x&1;
     for ( int i = t[0] + 1;i <= 24; ++i) t[i] = 0;
}
inline void Add( int x)
{
     ++num;
     v[num] = x^v[num - 1];
     ten_to_two(v[num],t);
     root[num] = Vc + 1;
     int las = root[num - 1];
     for ( int i = 24; i; --i) {
         tree[++Vc] = tree[las]; ++tree[Vc].size;
         if (t[i]) las = tree[las].rc, tree[Vc].rc = Vc + 1;
         else las = tree[las].lc, tree[Vc].lc = Vc + 1;
     }
     tree[++Vc] = tree[las]; ++tree[Vc].size;
}
inline int zero_num( const int lt, const int rt)
{ return tree[tree[rt].lc].size - tree[tree[lt].lc].size;}
inline int one_num( const int lt, const int rt)
{ return tree[tree[rt].rc].size - tree[tree[lt].rc].size;}
int Query( int lt, int rt, int x)
 
{
     ten_to_two(x,t);
     int ans = 0;
     for ( int i = 24; i; --i) {
         if (t[i]) {
             if (zero_num(lt,rt)) lt = tree[lt].lc,rt = tree[rt].lc;
             else lt = tree[lt].rc,rt = tree[rt].rc,t[i] = 0;
         } else {
             if (one_num(lt,rt))  lt = tree[lt].rc,rt = tree[rt].rc,t[i] = 1;
             else lt = tree[lt].lc,rt = tree[rt].lc;
         }
         if (t[i]) ans |= 1 << i-1;
     }
     return ans;
}
int main()
{
     int n,m;
     scanf ( "%d%d" ,&n,&m);
     root[0] = ++Vc;
     Add(0);
     for ( int i = 1,x; i <= n; ++i)
         { scanf ( "%d" ,&x);Add(x);}
     char c;
     for ( int x,l,r;m--;) {
         scanf ( "\n%c" ,&c);
         if (c == 'A' ) { scanf ( "%d" ,&x);Add(x);}
         else {
             scanf ( "%d%d%d" ,&l,&r,&x);
             printf ( "%d\n" ,Query(root[l - 1],root[r],x^v[num]));
         }
     }
}

 

转载于:https://www.cnblogs.com/lazycal/p/3252994.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值