• P5283 异或粽子


    https://www.luogu.com.cn/problem/P5283

    其实并不需要可持久化,只需要不同的 trie 就行了
    先把它来个异或前缀和,这样问题就转化为了求前 (k) 大的任意两数异或的和,记得要补一个 (0)
    因为异或有交换律,不妨先求前 (2k) 大的和,然后答案除以二,这样就不用考虑重复的问题了

    然后把每个数插入到 trie 上,这样可以 (O(log a_i)) 的求一个数与这个数列中其他数异或的第 (rank) 大的结果(考虑每一位是向和这个数在那一位相同的数字方向走,还是相异)
    然后开一个堆,首先把每个数和别的数异或的最大结果插入堆中,然后每次取出最大值,假设这是某个数与其他数异或的第 (rk) 大的,那么就答案加上它,并把这个数与其他数异或第 (rk+1) 大的插入对中

    另外手写对跑的真比 stl 的快得多,在不开O2的代码中大概第三左右

    #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,k;
    struct node{
    	int id,rk;
    	long long num;
    }heap[50000006];
    int size;
    struct tr{
    	tr *son[2];
    	int cnt;
    }dizhi[50000006],*root,*null;
    int tot;
    long long a[500006];
    inline void New(tr *&a){
    	a=&dizhi[++tot];
    	a->son[0]=a->son[1]=null;
    }
    #define MAX 33
    void insert(tr *tree,long long num,int pos){
    	tree->cnt++;
    	if(pos<0) return;
    	int nex=(num>>pos)&1;
    	if(tree->son[nex]==null) New(tree->son[nex]);
    	insert(tree->son[nex],num,pos-1);
    }
    long long ask(tr *tree,long long num,int rk,int pos){
    //		printf("rk = %d
    ",rk);
    	if(pos<0) return 0;
    	int nex=((num>>pos)&1)^1;
    	if(tree->son[nex]->cnt>=rk) return (1ll<<pos)+ask(tree->son[nex],num,rk,pos-1);
    	return ask(tree->son[nex^1],num,rk-tree->son[nex]->cnt,pos-1);
    }
    inline int operator >=(const node &a,const node &b){return a.num>=b.num;}
    inline void push(node x){
    	heap[++size]=x;
    	reg int i=size,fa;
    	while(i>1){
    		fa=i>>1;
    		if(heap[fa]>=heap[i]) return;
    		std::swap(heap[fa],heap[i]);i=fa;
    	}
    }
    inline node pop(){
    	node ret=heap[1];heap[1]=heap[size--];
    	reg int i=1,ls,rs;
    	while((i<<1)<=size){
    		ls=i<<1;rs=ls|1;
    		if(rs<=size&&heap[rs]>=heap[ls]) ls=rs;
    		if(heap[i]>=heap[ls]) break;
    		std::swap(heap[i],heap[ls]);i=ls;
    	}
    	return ret;
    }
    int main(){
    	n=read();k=read()*2;
    	null=&dizhi[0];null->son[0]=null->son[1]=null;
    	New(root);
    	insert(root,0,MAX);
    	for(reg int i=1;i<=n;i++){
    		a[i]=read()^a[i-1];
    		insert(root,a[i],MAX);
    	}
    	for(reg int i=0;i<=n;i++) push((node){i,1,ask(root,a[i],1,MAX)});
    	reg node now;long long ans=0;
    	while(k--){
    		now=pop();
    		ans+=now.num;
    		if(now.rk<n) push((node){now.id,now.rk+1,ask(root,a[now.id],now.rk+1,MAX)});
    	}
    	printf("%lld",ans>>1);
    	return 0;
    }
    
  • 相关阅读:
    scanf的参数类型自动转换
    Gedit中文乱码
    在VirtualBox的Ubuntu虚拟机中与母体Windows共享文件夹
    Win7安vc2008编译报LINK : fatal error LNK1000: Internal error during IncrBuildImage
    贝叶斯后验概率小记
    美国计算机专业最好的前20名学校
    Linux磁盘空间不够怎么办?
    Debian6.0装机过程
    把用户加到sudoers组中的方法
    vi命令手册
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/13817021.html
Copyright © 2020-2023  润新知