版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/89339304
题目大意:
https://www.luogu.org/problemnew/show/P5283
分析:
考虑对数列跑前缀异或和,然后相当于取两个数,把前
大的加起来。
可以像超级钢琴那道题一样,先把每个位置
结尾的序列,找到一个
且
最大。
再把所有
插入一个堆中,每次取出最大。如果取出的是第
个位置第
大的异或值,那就插入第
大的即可。
因为
,我们可以使得
的限制消掉,然后使
,最后答案除以
。
我们把所有数插入一个字典树,每次求某个数异或值第
大即可。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#define LL long long
const int maxn=5e5+7;
const int maxp=31;
using namespace std;
int n,m,root,cnt;
LL a[maxn],bit[maxp+1],ans;
struct node{
int l,r,data;
}t[maxn*maxp];
struct rec{
int x,k;
LL data;
};
bool operator <(rec a,rec b)
{
return a.data<b.data;
}
priority_queue <rec> q;
void ins(int &p,int d,LL x)
{
if (!p) p=++cnt;
t[p].data+=1;
if (d<0) return;
if (bit[d]&x) ins(t[p].r,d-1,x);
else ins(t[p].l,d-1,x);
}
LL getrank(int p,int d,LL x,int k) //求异或第k大
{
if (d<0) return 0;
if (bit[d]&x)
{
if (k<=t[t[p].l].data) return getrank(t[p].l,d-1,x,k);
else return getrank(t[p].r,d-1,x,k-t[t[p].l].data)+bit[d];
}
else
{
if (k<=t[t[p].r].data) return getrank(t[p].r,d-1,x,k)+bit[d];
else return getrank(t[p].l,d-1,x,k-t[t[p].r].data);
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
for (int i=1;i<=n;i++) a[i]^=a[i-1];
m<<=1;
bit[0]=1;
for (int i=1;i<=maxp;i++) bit[i]=bit[i-1]*2;
for (int i=0;i<=n;i++) ins(root,maxp,a[i]);
for (int i=0;i<=n;i++)
{
q.push((rec){i,1,a[i]^getrank(root,maxp,a[i],1)}); //插入异或第1大
}
for (int i=1;i<=m;i++)
{
rec d=q.top(); //取出堆顶
q.pop();
ans+=d.data;
q.push((rec){d.x,d.k+1,a[d.x]^getrank(root,maxp,a[d.x],d.k+1)}); //插入对应位置第k+1大
}
printf("%lld\n",ans/2);
}