bzoj 3261最大异或和

Description

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

Input

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

Output

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

 

 

题意:应该很清楚了;

题解:

①原题中的式子比较假: 化成max( (a[n] xor x)xor a[p-1] ) p∈[l,r];

②前面的now = (a[n] xor x)是一个定值,建立一颗字典树,从高位到低位考虑,一定是尽量选和now的那一位相反的数,对前缀建树,如果类似于普通01字典树,只是把一个数字插入经过的所有节点++,这个可持久的结构维护sum[r]-sum[l-1]第i位的节点值位0或1的个数;

③查询时,从高到低枚举位数j,如果和now的j位相反的数个数在sum[r]-sum[l-1]内存在,则进入这个节点,否则进入另一个节点执行同样操作,每次更新选择的数的j位

    

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=600010,S = 30;
 6 int n,m,a[N],b[N],x,rt[N],sz,ch[N*S][2],sum[N*S];
 7 char gc(){
 8     static char *p1,*p2,s[1000000];
 9     if(p1==p2) p2=(p1=s)+fread(s,1,1000000,stdin);
10     return(p1==p2)?EOF:*p1++;
11 }
12 int rd(){
13     int x = 0; char c = gc();
14     while(c<'0'||c>'9') c = gc();
15     while(c>='0'&&c<='9') x = x * 10 + c - '0',c = gc();
16     return x;
17 }
18 bool opt(){
19     char c = gc();
20     while(c!='A'&&c!='Q') c = gc();
21     return c=='A';
22 }
23 int ins(int last,int val){ 
24     int k,ret; k = ret = ++sz; 
25     for(int i = 23;i >= 0;i--){
26         sum[k] = sum[last] + 1; ch[k][0] = ch[last][0]; ch[k][1] = ch[last][1];
27         int d = (val>>i)&1;
28         k = ch[k][d] = ++sz; last = ch[last][d];
29     }
30     sum[k] = sum[last] + 1;
31     return ret;
32 }
33 int query(int k1,int k2,int val){
34     int ret = 0;
35     for(int i = 23;i >= 0;i--){
36         int d = (val>>i)&1;
37         if(sum[ch[k2][d^1]]-sum[ch[k1][d^1]]>0) 
38         ret|=(1<<i),k1=ch[k1][d^1],k2=ch[k2][d^1];
39         else k1=ch[k1][d],k2=ch[k2][d];
40     }
41     return ret;
42 }
43 int main()
44 {    freopen("bzoj3261.in","r",stdin);
45     freopen("bzoj3261.out","w",stdout);
46     n = rd()+1; m = rd();
47     rt[1]=ins(rt[0],b[1]);
48     for(int i = 2;i <= n;i++) b[i] = b[i-1]^rd(),rt[i]=ins(rt[i-1],b[i]); 
49     char s[10];
50     for(int i = 1;i <= m;i++){
51         if(opt()){
52             n++; b[n]=b[n-1]^rd();
53             rt[n]=ins(rt[n-1],b[n]); 
54         }
55         else {
56             int l = rd(),r = rd(),x = rd();
57             int tmp = query(rt[l-1],rt[r],b[n]^x);
58             printf("%d\n",tmp);
59         }
60     }
61     return 0;
62 }//by tkys_Austin;

 

转载于:https://www.cnblogs.com/Paul-Guderian/p/8645650.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值