1. 程式人生 > 實用技巧 >P5283 異或粽子

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\n",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;
}