bzoj3261 最大异或和(可持久化Trie)

来源:互联网 发布:c专家编程pdf 非扫描 编辑:程序博客网 时间:2024/05/15 09:36

bzoj3261 最大异或和

原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3261

题意:
给定一个非负整数序列 {a},初始长度为 N。
有 M个操作,有以下两种操作类型:

1 、A x:添加操作,表示在序列末尾添加一个数 x,序列的长度 N+1。
2 、Q l r x:询问操作,你需要找到一个位置 p,满足 l<=p<=r,使得:

a[p] xor a[p+1] xor … xor a[N] xor x 最大,输出最大是多少。

数据范围
N,M<=300000 ,0<=a[i]<=10^7

题解:
(重写数组版…)
因为有Add操作,维护后缀的Trie是困难的,于是考虑转化成前缀。
那么:
令pre[i]=a[1]^a[2]^……a[i]
a[p] ^ a[p+1] ^ … ^ a[N] ^ x=pre[p-1]^pre[n]^x
于是变成可持久化Trie维护各个位置的前缀异或和,在其中查询x^pre[n]与这些前缀中的一个能够异或出的最大值。

注意:
原来查询 [L,R]区间,转化后是 [L-1,R-1]的前缀,然后可持久化Trie又要用到root[(L-1)-1],
如果从1开始这就有-1了,于是从2开始,先在root[1]加个0进去。


YYR学长的平方分割大法:

我们先来考虑,若没有添加操作显然可以将所的后缀异或和建成持久化Trie树来做。
那如果添加了 k个元素呢?
查询时可以将 x异或上添加的 k个元素,再到Trie中查询。

实际上也就是每添加 lim 个元素后,重新构建数据结构。
假设查询和添加操作次数相近,那么这道题样做的话
建树总复杂度 O(N * 位数 * (N/ lim )) ,
查询总复杂度 O(N * (lim + 位数 )) 。
利用前面的方法,可以得到 lim 取 sqrt (N * (N * 位数 ) 时较优。
复杂度O(N * sqrt sqrt (N * (N * 位数 ))


代码:

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;const int P=30;const int N=600005;struct node{    int ch[2],size;}tr[N*35];int n,m,tail=0,a[N],root[N];void insert(int pre,int &now,int val,int p){    now=++tail; tr[now]=tr[pre]; tr[now].size++;    if(p==-1) return;    int opt=(val>>p)&1;    insert(tr[pre].ch[opt],tr[now].ch[opt],val,p-1);}int query(int pre,int nd,int val,int p,int ans){    if(p==-1) return ans;    int opt=(val>>p)&1; opt=opt^1;    int ns=tr[tr[nd].ch[opt]].size; int ps=tr[tr[pre].ch[opt]].size;    if(ns-ps>0) return query(tr[pre].ch[opt],tr[nd].ch[opt],val,p-1,ans+(1<<p));    else return query(tr[pre].ch[opt^1],tr[nd].ch[opt^1],val,p-1,ans);}int main(){    scanf("%d%d",&n,&m); n++;    for(int i=2;i<=n;i++) scanf("%d",&a[i]); a[0]=a[1]=0;    for(int i=2;i<=n;i++) a[i]=a[i-1]^a[i];    root[0]=0; tr[0].ch[0]=tr[0].ch[1]=tr[0].size=0;    for(int i=1;i<=n;i++) insert(root[i-1],root[i],a[i],P);    while(m--)    {        char opt[5]; int x,l,r; scanf("%s",opt);        if(opt[0]=='A')        {            n++; scanf("%d",&a[n]); a[n]^=a[n-1];            insert(root[n-1],root[n],a[n],P);        }        else        {            scanf("%d%d%d",&l,&r,&x);            x=x^a[n];            printf("%d\n",query(root[l-1],root[r],x,P,0));        }    }    return 0;}
原创粉丝点击