[BZOJ3261]-最大异或和-可持久化trie

说在前面

「可持久化」这个词含义还真多= =
其实可持久化trie实际上就是主席trie…?


题目

BZOJ3261传送门

题面

给定一个非负整数序列 {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,含义如问题描述所示。
第二行包含N个非负整数,表示初始的序列A。
接下来M行,每行描述一个操作,格式如题面所述。

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


解法

因为是在区间里查询,比较容易想到可持久化数据结构
但是询问操作查询的是后缀和,直接维护比较麻烦
然而异或是可以抵消的,也就是说a[p] xor … xor a[N]等价于( a[1] xor … xor a[N] ) xor ( a[1] xor .. a[p-1] )
所以可以把询问转化为前缀和查询,然后就可以瞎搞搞了=w=

代码实现的时候注意,如果L=1需要特判异或全部的情况。写了代码就会理解这句话的意思了


下面是自带大常数的代码

/**************************************************************
    Problem: 3261
    User: Izumihanako
    Language: C++
    Result: Accepted
    Time:4860 ms
    Memory:171916 kb
****************************************************************/

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , M , rcnt , sum ;
struct Node{
    int cnt ;
    Node *ch[2] ;
}w[600005*24] , *tw = w , *root[600005] , null ;

void Insert( Node *las , Node *&nd , int num , int ws ){
    nd = ++tw , nd->cnt = las->cnt + 1 ;
    if( ws == -1 ) return ;
    if( num&( 1<<ws ) ){
        nd->ch[0] = las->ch[0] ;
        Insert( las->ch[1] , nd->ch[1] , num , ws - 1 ) ;
    } else{
        nd->ch[1] = las->ch[1] ;
        Insert( las->ch[0] , nd->ch[0] , num , ws - 1 ) ;
    }
}

int Query( Node *lf , Node *rg , int x ){
    for( int i = 23 ; i >= 0 ; i -- ){
        int lsiz = rg->ch[0]->cnt - lf->ch[0]->cnt ,
            rsiz = rg->ch[1]->cnt - lf->ch[1]->cnt ;
    //  printf( "hg(%d) lsiz(%d) rsiz(%d)\n" , i , lsiz , rsiz ) ;
        if( x&(1<<i) ){
            if( lsiz ) lf = lf->ch[0] , rg = rg->ch[0] ;
            else       lf = lf->ch[1] , rg = rg->ch[1] , x^=(1<<i) ;
        } else {
            if( rsiz ) lf = lf->ch[1] , rg = rg->ch[1] , x^=(1<<i) ;
            else       lf = lf->ch[0] , rg = rg->ch[0] ;
        }
    }
    return x ;
}

void solve(){
    char opt[5] ;
    for( int i = 1 , L , R , x ; i <= M ; i ++ ){
        scanf( "%s" , opt ) ;
        switch( opt[0] ){
            case 'A':{
                scanf( "%d" , &x ) ;
                sum ^= x , rcnt ++ ;
                Insert( root[rcnt-1] , root[rcnt] , sum , 23 ) ;
                break ;
            }
            default :{
                scanf( "%d%d%d" , &L , &R , &x ) ;
                int ans = Query( root[ max(L-2,0) ] , root[R-1] , x^sum ) ;
                if( L == 1 ) ans = max( ans , x ^ sum ) ;
                printf( "%d\n" , ans ) ;
            }
        }
    }
}

int main(){
    null.ch[0] = null.ch[1] = &null ;
    root[0] = ++tw ; *root[0] = null ;
    scanf( "%d%d" , &N , &M ) ;
    for( int i = 1 , tmp ; i <= N ; i ++ ){
        scanf( "%d" , &tmp ) ;
        sum ^= tmp , rcnt ++ ;
        Insert( root[rcnt-1] , root[rcnt] , sum , 23 ) ;
    }
    solve() ;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值