• P4735 最大异或和(可持久化 trie)


    https://www.luogu.com.cn/problem/P4735
    https://darkbzoj.tk/problem/3261

    (sum_i) 表示前 (i) 个数的异或和,转换那个式子为 (sum_n operatorname{XOR} x operatorname{XOR} sum_i,i in [l-1,r-1])
    其中的 (sum_n operatorname{XOR} x operatorname{XOR}) 是个定值,也就是找一个 (i) 使得这个式子最大
    由于这是异或运算,从高位向低位考虑,贪心的让每一位尽量和 (sum_n operatorname{XOR} x operatorname{XOR}) 的结果不一样
    可以把这 (n)(sum) 值插入到一个 trie 上
    就是如果第 (pos) 位结果是 (k),那么如果 ([l-1,r-1]) 中有一个 (sum_j) 使得它的第 (pos) 位是 (k operatorname{XOR} 1),那么就选他,答案加 (2^{pos}),否则选另一个,答案不加

    然后想看区间内某个位置上 (1/0) 的数有没有,其实就是看这个位上是 (1/0) 的数的个数的前缀和,这就用到了 trie 的可持久化,每插入一个数时,对于某一位,把它所对应的那个数字的儿子(比如这个位置是 (1),就是 son[1])开一个新的内存,个数加一,另一个儿子还是指向原来那个树的儿子(同时个数也就不变)
    其实就是 (n) 个根,每个根都对应一个完整的树,但这些树会有一些节点是共用的

    要注意在最前面插入一个 (0),来应对 (l=0) 的情况,由于每个新插入的数的每一位都要开辟一个新内存,所以空间是 (O((n+m)log a_i))
    为了防止判断空指针带来的代码细节增多和常数增大,用一个自己设定的 null 来作为空指针

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<map>
    #include<iomanip>
    #include<cstring>
    #define reg register
    #define EN puts("")
    inline long long read(){
    	register long long x=0;register int y=1;
    	register char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    int n,m;
    struct tr{
    	tr *son[2];
    	int cnt;
    }dizhi[20000006],*root[600006],*null;
    int tot=-1;
    inline tr *New(){
    	tr *ret=&dizhi[++tot];
    	ret->son[0]=ret->son[1]=null;
    	return ret;
    }
    void build(tr *last,tr *tree,int num,int pos){
    	tree->cnt=last->cnt+1;
    	if(pos<0) return;
    	int nex=(num>>pos)&1;
    	tree->son[nex]=New();
    	tree->son[nex^1]=last->son[nex^1];
    	build(last->son[nex],tree->son[nex],num,pos-1);
    }
    int ask(tr *left,tr *right,int num,int pos){
    	if(pos<0) return 0;
    	int nex=((num>>pos)&1)^1;
    //		printf("right->son[0]=%d , right->son[1]=%d
    ",right->son[0]->cnt,right->son[1]->cnt);
    //		printf("left->son[0]=%d , left->son[1]=%d
    
    ",left->son[0]->cnt,left->son[1]->cnt);
    	if(right->son[nex]->cnt>left->son[nex]->cnt) return (1<<pos)+ask(left->son[nex],right->son[nex],num,pos-1);
    	return ask(left->son[nex^1],right->son[nex^1],num,pos-1);
    }
    #define MAX 24
    int main(){
    	n=read();m=read();
    	int sum=0;
    	null=&dizhi[++tot];
    	null->son[0]=null->son[1]=null;
    	root[0]=New();
    	root[1]=New();//新加入一个 root[1],值是 0,以后插入的 a[i] 存入 root[i+1]
    	build(root[0],root[1],0,MAX);
    	n++;
    	for(reg int i=2;i<=n;i++){
    		sum^=read();
    		root[i]=New();build(root[i-1],root[i],sum,MAX);
    	}
    	reg char c;reg int l,r;
    	while(m--){
    		c=getchar();
    		while(c!='Q'&&c!='A') c=getchar();
    		if(c=='A'){
    			root[++n]=New();
    			sum^=read();
    			build(root[n-1],root[n],sum,MAX);
    		}
    		else{
    			l=read();r=read();
    			printf("%d
    ",ask(root[l-1],root[r],sum^read(),MAX));
    			//其实是查询区间 [l-1,r-1],因为是前缀和所以是 [l-2,r-1],但由于最前面插入了一个 0,整体后移一个,是 [l-1,r]
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    java打包与热部署 爱上
    分组后最新的记录 爱上
    Js操作Excel常用方法 GO
    查找父元素和子元素 GO
    DataView不能按中文排序问题解决 GO
    浮动层居中的对话框效果演示 GO
    ajax form提交 GO
    SQL Server智能提示插件下载
    提高代码质量的三要素
    Div的宽度与高度设定100%
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/13775999.html
Copyright © 2020-2023  润新知