Problem I: qwb VS 去污棒
Time Limit: 2 Sec Memory Limit: 256 MBSubmit: 64 Solved: 20
[ Submit][ Status][ Web Board]
Description
qwb表白学姐失败后,郁郁寡欢,整天坐在太阳底下赏月。在外人看来,他每天自言自语,其实他在和自己的影子“去污棒”聊天。
去污棒和qwb互相出题考验对方,去污棒问了qwb这样一个问题:
现已知一个有n个正整数的序列a[1],a[2]...a[n],接下来有m个操作
suf[t]表示从t开始的后缀的异或和,即 suf[t]=a[t] xor a[t+1] xor ...xor a[len],len为序列长度。
去污棒和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
1
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:
6
9
31
这道题,区间异或值最大!一眼看上去似乎没什么思路。但是在想一想,无论多少个值异或,二进制的位数也不会多于最大的数的位数。所以就有一种求区间最大值的感觉
!果断想到可持久化线段树--主席树!!这个题用主席树还得想一下,不能直接枚举区间,然后二分。因为异或值去看二进制比较舒服,题目又保证不大于2^24.所以直接从每个异或值得二进制的第24位开始看,是0还是1.。0就更新左树,1就更新右树!查询当然就得反过来!,为0就看右树是否更新过,如果更新了,就继续查右,否则就查左!
异或值为1同理。左树跟新过就查左,否则查右!至此,讨论完毕!!!注意坑点, 要把0点一开始存进去。。然后每次查l,r区间,记得往左推一位!!!
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 600010
#define N (1<<24)
using namespace std;
int lch[N],rch[N],size[N],root[maxn];
int n,m,tot,T,sum,op;
int modify(int pre,int i,int x)
{
int now=++tot;
if (i==0){lch[now]=rch[now]=0;size[now]=size[pre]+1;}
else
{
int mid=((x>>(i-1))&1);
if (mid==0)
{
rch[now]=rch[pre];lch[now]=modify(lch[pre],i-1,x);
}
else
{
lch[now]=lch[pre];rch[now]=modify(rch[pre],i-1,x);
}
size[now]=size[lch[now]]+size[rch[now]];
}
return now;
}
int query(int root1,int root2,int i,int x)
{
if (i==0) return 0;
int mid=((x>>(i-1))&1);
if (mid==0)
{
if (size[rch[root2]]-size[rch[root1]]>0) return (1<<(i-1))+query(rch[root1],rch[root2],i-1,x);
else return query(lch[root1],lch[root2],i-1,x);
}
else
{
if (size[lch[root2]]-size[lch[root1]]>0) return (1<<(i-1))+query(lch[root1],lch[root2],i-1,x);
else return query(rch[root1],rch[root2],i-1,x);
}
}
int main()
{
int pp;scanf("%d",&pp);
for (int p=1;p<=pp;p++)
{
printf("Case %d:\n",p);
scanf("%d%d",&n,&T);tot=0,sum=0;
root[0]=lch[0]=rch[0]=size[0]=0;
root[0]=modify(root[0],25,0);
for (int i=1;i<=n;i++)
{
int x;scanf("%d",&x);sum^=x;
root[i]=modify(root[i-1],25,sum);
}
while (T--)
{
int l,r,x;scanf("%d",&op);
if (op==1)
{
scanf("%d",&x);sum^=x;n++;
root[n]=modify(root[n-1],25,sum);
}
else
{
scanf("%d%d%d",&l,&r,&x);l--;r--;
printf("%d\n",query(root[l-1],root[r],25,sum^x));
}
}
}
return 0;
}