题意:给定长度为nnn的序列aaa,求前kkk大的区间异或和之和。
n≤5×105,k≤min(n(n−1)2,2×105),ai<232n\leq 5\times 10^5,k\leq \min(\frac{n(n-1)}2,2\times10^5),a_i<2^{32}n≤5×105,k≤min(2n(n−1),2×105),ai<232
原来省选题这么裸,i了i了
显然先求一个前缀异或,然后区间异或转换为两个数的异或
把kkk乘上222,最后答案再除回来,这样没有了顺序限制
然后对于这种前kkk大之和或某些第kkk大的问题的套路:
把所有情况分成sss个集合,对每个集合找到一种方式,可由一个数在O(m)O(m)O(m)时间内得到nnn个比这个数小的数,且每个数都会被遍历到,然后用堆维护这个东西。这样复杂度是O(s+kmlog(s+kn))O(s+km\log(s+kn))O(s+kmlog(s+kn))
本题中集合为每个数和其他所有数的异或。建个字典树,然后每次暴力询问第kkk大即可。
复杂度O(nlogV+klognlogV)O(n\log V+k\log n\log V)O(nlogV+klognlogV)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <queue>
#include <utility>
#define MAXN 500005
using namespace std;
typedef unsigned int uint;
typedef long long ll;
uint read()
{uint ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
int ch[MAXN<<5][2],siz[MAXN<<5],cnt=1;
void insert(uint v)
{int x=1;for (int i=31;i>=0;i--){int& nxt=ch[x][(v>>i)&1];if (!nxt) nxt=++cnt;++siz[x],x=nxt;}++siz[x];
}
uint query(uint v,int k)
{uint ans=0;int x=1;for (int i=31;i>=0;i--){int d=(v>>i)&1;if (ch[x][d^1]&&k<=siz[ch[x][d^1]]) ans|=1<<i,x=ch[x][d^1];else k-=siz[ch[x][d^1]],x=ch[x][d];}return ans;
}
typedef pair<uint,int> pi;
priority_queue<pi> q;
int rk[MAXN];
uint a[MAXN];
int main()
{int n,k;scanf("%d%d",&n,&k);k<<=1;for (int i=1;i<=n;i++) a[i]=a[i-1]^read();for (int i=0;i<=n;i++) insert(a[i]);ll ans=0;for (int i=0;i<=n;i++) q.push(make_pair(query(a[i],rk[i]=1),i));while (k--){pi t=q.top();q.pop();ans+=t.first;q.push(make_pair(query(a[t.second],++rk[t.second]),t.second));}printf("%lld\n",ans>>1);return 0;
}