【简】题解 P5283 [十二省联考2019]异或粽子
传送门:P5283 [十二省联考2019]异或粽子
题目大意:
给一个长度为n的数列,找到异或和为前k大的区间,并求出这些区间的异或和的代数和。
QWQ:
考试时想到了前缀异或 想到了对每个数按二进制拆分 最高位取一定比前面所有取优
但是呆住了 没有想到是对前缀异或拆分
对于位运算等操作可以考虑 线性基和trie
因为 ai xor aj=aj xor ai 所以吧这种情况算进去就取ans/2
因为 i=j 时异或为0是最小的 不会影响答案
把各个前缀异或插进数组
询问强制以每个点为前面的一个端点 查询第x大的值
先把所有的第1大的插进堆里
每次取堆中最大 统计答案
设当前取出的最大是强制的区间第x大 把区间第x+1大插入队里
代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define C getchar()-48 inline ll read() { ll s=0,r=1; char c=C; for(;c<0||c>9;c=C) if(c==-3) r=-1; for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c; return s*r; } #define R register const ll N=20000000+10; ll n,k,ans; ll a[N],sum[N],sz[N]; ll tr[N][2],top; struct xin{ ll w,cs,v; friend bool operator < (xin a,xin b) { return a.v<b.v; } }; priority_queue<xin>q; inline void into(ll v) { int u=0; for(int i=31;i>=0;i--) { ll tmp=(v>>i)&1;sz[u]++; if(!tr[u][tmp]) tr[u][tmp]=++top; u=tr[u][tmp]; } sz[u]++; } inline ll ask(ll v,int cs) { ll u=0,ans=0; for(int i=31;i>=0;i--) { ll tmp=(v>>i)&1; if(!tr[u][tmp^1]){u=tr[u][tmp];continue;} if(cs<=sz[tr[u][tmp^1]]){u=tr[u][tmp^1];ans|=1LL<<i;continue;} if(cs>sz[tr[u][tmp^1]]){cs-=sz[tr[u][tmp^1]];u=tr[u][tmp];continue;} } return ans; } int main() { freopen("xor.in","r",stdin); freopen("xor.out","w",stdout); n=read();k=read();k<<=1; for(R int i=1;i<=n;i++) a[i]=read(); for(R int i=1;i<=n;i++) sum[i]=sum[i-1]^a[i]; for(int i=0;i<=n;i++) into(sum[i]); for(int i=0;i<=n;i++) q.push((xin){i,1,ask(sum[i],1)}); for(int i=1;i<=k;i++) { xin tmp=q.top();q.pop();ans+=tmp.v; if(tmp.cs<n) q.push((xin){tmp.w,tmp.cs+1,ask(sum[tmp.w],tmp.cs+1)}); } cout<<(ans>>1)<<endl; return 0; }