题目分析:
我们知道Trie树可以对某一个值查询与它异或第k大的值。
但是全局前k大就懵逼了。
这时候就有一个神奇的技巧套路,对每个右端点,找到与它异或最大的左端点,将这n个值放入优先队列中。
这时候队首一定是全局最大值,加进答案中,pop掉,然后再找到这个值对应的右端点的左端点中第二大的,放入优先队列中。
这时候的队首一定是全局第二大。
一直这样做k次即可。
找右端点对应的第几大左端点就用可持久化Trie,可以每次删除然后找最大,也可以每次找第i大,弹出一次下一次就找第i+1大。
1<<32位见祖宗系列(考场爆零选手已无力吐槽恶心数据范围)。。。
要是我以后到了清华一定找到出题人把他挂起来裱。。。
这题还是BZOJ原题。。。 BZOJ3689异或之
Code:
#include<cstdio>
#include<cctype>
#include<queue>
#define maxn 500005
#define maxp maxn*64
using namespace std;
template<class T>inline void read(T &a){
char c;while(!isdigit(c=getchar()));
for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
typedef unsigned int ui;
int n,k,rt[maxn],ch[maxp][2],siz[maxp],tot;
ui a[maxn];
struct node{
ui x; int pos;
bool operator < (const node &p)const{return x<p.x;}
};
priority_queue<node>q;
void insert(int &now,int i,ui x,int d){
siz[++tot]=siz[now]+d,ch[tot][0]=ch[now][0],ch[tot][1]=ch[now][1];
now=tot;
if(i==-1) return;
insert(ch[now][x>>i&1],i-1,x,d);
}
ui find(int now,ui x){
ui ret=0;
for(int i=31,v;i>=0;i--,now=ch[now][v]){
v=!(x>>i&1);
if(siz[ch[now][v]]) ret|=1u<<i;
else v=!v;
}
return ret;
}
int main()
{
read(n),read(k);
insert(rt[0],31,0,1);
for(int i=1;i<=n;i++)
read(a[i]),a[i]^=a[i-1],
q.push((node){find(rt[i-1],a[i]),i}),insert(rt[i]=rt[i-1],31,a[i],1);
long long ans=0; int x; ui y,tmp;
while(k--){
ans+=(tmp=q.top().x),x=q.top().pos,y=0;q.pop();
for(int i=31;i>=0;i--) y|=(a[x]&(1u<<i))^(tmp&1u<<i);
insert(rt[x-1],31,y,-1);
if(siz[rt[x-1]]) q.push((node){find(rt[x-1],a[x]),x});
}
printf("%lld",ans);
}