BZOJ 3261 浅谈可持久化TRIE树最大连续异或和

来源:互联网 发布:仙侠世界cos捏脸数据 编辑:程序博客网 时间:2024/06/01 10:05

这里写图片描述
世界真的很大
trie树贪心求最大异或和大概也就是那么回事了
但是对于区间的查询就不是那么容易的了
考虑主席树的思想,怎么得到区间的值域的
这就是可持久化的trie树
说来容易
指针教做人哪
看题先:
description:

给定一个非负整数序列 {a},初始长度为 N。       有   M个操作,有以下两种操作类型:1 、A x:添加操作,表示在序列末尾添加一个数 x,序列的长度 N+12 、Q l r x:询问操作,你需要找到一个位置 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行,每行一个整数表示询问的答案。

首先考虑怎么把连续的异或和转化掉
考虑连续一段的数字之和的转化,除了线段树之外的高级东西,前(后)缀和这个东西在这种不修改的情况下就要优秀的多了
[L,R]的数字之和可以转化成sum[R]-sum[L-1]或last[L]-last[R+1],而考虑异或和这种东西,如果一个数连续异或偶数次就是0,而任何数异或0都是本身,除了0自己
所以如果处理一段连续的异或和的话,就可以考虑用异或前(后)缀和来处理了,而且由于有添加的操作,只会影响后缀而不会影响前缀,如果使用后缀的话每次修改都需要把前面的后缀全部修改一遍,这是不能接受的
既然能达到一样的区间处理效果,尽管每次求的是最大的后缀,但我们仍然选择用前缀维护
每次就用X^=sum[n],再用这样的X在L到R范围内找一个sum使其异或和最大
这就转化成了在一堆数里面选一个数与指定数异或和最大的经典trie树贪心的问题了
但我们不可能每次询问都对指定区间建一颗trie树,这样询问的代价就是O(n)的了,这是不能接受的。我们希望能够预处理出每次询问的trie树,而询问优势在线的,
可持久化trie树就应运而生了
考虑可持久化线段树,主席树的实现方式,将R的trie树减去L-1的trie树,类比主席树,得到的就应该是L到R区间的所有数得到的trie树,而这样的一颗颗trie树是可以预处理的,而每次在数列末尾加数字时再新建一颗trie树,就可以动态的维护这样的一颗颗树了
大概思想是有了,接下来考虑实现方法
还是要类比主席树
每一个点都建一颗trie树,存的是1到这个点的所有数的二进制的01串,而对于每一个点都重新建一颗这样的树,时间是O(n^2)的,不能接受的,而类比主席树,每一个点对应的树和前一颗树不同的地方只有一个数而已,完全没有必要重新建一棵树,这样空间时间上都能省一笔
可持久化trie树的大概思想好像差不多了
那么我们现在需要考虑的就只有具体的建树思路了
对于相邻的两棵树,只有一个数不同而已,一个数在trie上对应的就是一条链,建树时我们沿着这条链走,把这条链构造进新树里,其余结构全部指向旧树
就是说新树只有这一条链的部分是新建的,而其他部分全部与旧树共享,类比主席树
还剩下查询操作,关键是判断在当前区间内有没有trie树的这条链,考虑主席树是怎么做的,对每一个节点开一个域cnt,记录这个节点以下有几个数,每次按trie贪心的方法查询时,判断R树的节点cnt值减去L-1树的节点的cnt值为不为0,不为0就说明区间内有这么一个数可以走
类比主席树
大概想一下,具体化一下具体的代码就可以开始写的,还是挺好写的,只能说指针教做人哪
学新东西时还是注意一下细节
首先是指针写法,防RE的办法就不说了,很简单,注意trie树insert时使用的是递归还是while,是有区别的,while需要另设一个指针,不能直接用root跳
还有就是边界问题,一定要在trie树里一开始插入一个0,这很重要,因为如果取1到n为值的话,应该是sum[n]^sum[0],如果trie树里没有0的话,就无法实现这一步操作,我的做法是直接多建一棵树,往里面插值0,然后将n棵树依次往后推一位,这样第一棵树的值就是0了,每次查的时候就查L-1到R就行了,不然应该是L-2到R-1才对(这个也很重要,因为L到R选择一个后缀,是指用1到X的异或和异或上sum[L-1]到sum[R-1],而想要得到包含sum[L-1]和sum[R-1],不包含sum[R] 的trie树,就要用第R-1颗树减去L-2颗树,而我们在之前多建了一个点,所以查R到L-1就行了)
完整代码:

#include<stdio.h>#include<cstdio>#include<algorithm>using namespace std;struct node{    int cnt;    node *ch[2];}pool[20000010],*tail=pool,*root[600010],*null;int n,m,sum[600010];char ss[10];node *newnode(){    node *nd=++tail;    nd->cnt=0;    nd->ch[0]=nd->ch[1]=null;    return nd;}void init(){    null=++tail;    null->cnt=0;    null->ch[0]=null->ch[1]=null;}void insert(node *ne,node *&nd,int x){    nd=newnode();    node *now=nd;    for(int i=24;i>=0;i--)    {        now->ch[0]=ne->ch[0],now->ch[1]=ne->ch[1];        now->cnt=ne->cnt+1;        int idx=(x&(1<<i))>0;        now->ch[idx]=newnode();        now=now->ch[idx];ne=ne->ch[idx];    }    now->cnt=ne->cnt+1;}int query(node *ne,node *nd,int x){    int num=0;    for(int i=24;i>=0;i--)    {        int idx=(x&(1<<i))>0;        if(nd->ch[!idx]->cnt-ne->ch[!idx]->cnt)        {            num|=(1<<i);            ne=ne->ch[!idx],nd=nd->ch[!idx];        }        else            ne=ne->ch[idx],nd=nd->ch[idx];    }    return num;}int main(){    init();    scanf("%d%d",&n,&m);    n++;    for(int i=2;i<=n;i++)    {        scanf("%d",&sum[i]);        sum[i]^=sum[i-1];    }    root[0]=newnode();    for(int i=1;i<=n;i++)        insert(root[i-1],root[i],sum[i]);       while(m--)    {        scanf("%s",ss);        if(ss[0]=='A')        {            scanf("%d",&sum[++n]);            sum[n]^=sum[n-1];            insert(root[n-1],root[n],sum[n]);        }        else        {            int L,R,X;            scanf("%d%d%d",&L,&R,&X);            X^=sum[n];            printf("%d\n",query(root[L-1],root[R],X));        }       }       return 0;}/*Whoso pulleth out this sword from this stone and anvil is duly born King of all England*/

嗯,就是这样

原创粉丝点击