LOJ-3048
题意
给一个长度为 n 序列 a,让你求前 k 大区间异或和 的和,这 k 个区间 [li,ri] 要满足 1≤l≤r≤n 且没有区间相同。
1≤n≤5×105,1≤k≤min{2n(n−1),2×105},0≤ai<232
题解
类似于 [NOI2010]超级钢琴 (那道题求区间和),先处理出异或前缀和 sn=⨁i=1nai,对于一个右端点 r ,我们首先要找到使 sr⊕sl 最大的 l,(l∈[0,r]) ,处理异或和最大可以用 01tire 解决,这里有区间的限制要 可持久化,类似于主席树的操作,假设查询区间 [L,R] 则 root[R],root[L−1] 同时搜,root[R] 记录的个数减去 root[L−1] 记录的个数即为 [L,R] 区间的信息。
这样我们能得到对于每个右端点,使异或和最大的左端点,假设为 ps,进一步,此时我们求出 ps∈[L,R] 使 sr⊕sps 最大,那么对于 ps1∈[L,ps−1],ps2∈[ps+1,R] ,必然 sr⊕sps1,sr⊕sps2≤sr⊕sps 。也就是说更新完 ps 后才可能更新 ps1,ps2 。
具体做法:
1.考虑开一个大根堆,以异或和大小为序,枚举一遍 r ,找出 ps 和 val(val=maxsr⊕sp,ps∈[0,r] )(可持久化01trie解决) 把 (val,ps,L,R,r) 这样的五元组插入堆中。L,R 表示 ps 的范围(这里 L=0,R=r)
2.每次从堆顶取出元素 (val,ps,L,R,r) ,然后 ans+=val ,分别在 [L,ps−1],[ps+1,R] 中找 ps1,ps2 使 sr⊕sps1/2 最大,再向堆中插入 (val1,ps1,L,ps−1,r),(val2,ps2,ps+1,R,r) 。
3.重复 2 步骤直到取出元素 =k
CODE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| #include <bits/stdc++.h> #define ls(p) t[p].ch[0] #define rs(p) t[p].ch[1] #define s(p,d) t[p].ch[d] using namespace std; typedef unsigned int ui; typedef long long ll; template<typename T> inline void red(T &x) { x=0;bool fg=0;char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') fg^=1;ch=getchar();} while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(T)(ch^48),ch=getchar(); if(fg) x=-x; } const int N = 500010; struct node{int ch[2],siz,pos;}t[N*33]; struct rec{ int id,L,R,ps;ui val; rec(int _id,int _L,int _R,int _ps,ui _val) {id=_id,L=_L,R=_R,ps=_ps,val=_val;} bool operator<(const rec a) const {return val<a.val;} }; int root[N],n,K,tot; ui a[N]; int newnode(int _ps) { tot++;ls(tot)=rs(tot)=0;t[tot].pos=_ps; return tot; }
void insert(int p,int q,ui num,int _ps) { bool d; t[q].siz=t[p].siz+1; for(int i=31;i>=0;--i) { d=(num>>i)&1; t[q].ch[d]=newnode(_ps); t[q].ch[d^1]=t[p].ch[d^1]; p=t[p].ch[d];q=t[q].ch[d]; t[q].siz=t[p].siz+1; } } void build() { root[0]=newnode(0); int p=root[0]; t[p].siz=1; for(int i=0;i<=31;++i) { ls(p)=newnode(0); p=ls(p); t[p].siz=1; } }
ui query(int L,int R,ui k,int &ps) { int p=(L==0)?0:root[L-1],q=root[R]; bool d;ui ret=0; for(int i=31;i>=0;--i) { d=(k>>i)&1; if(t[s(q,d^1)].siz-t[s(p,d^1)].siz>0) { ret|=(1u<<i); p=s(p,d^1);q=s(q,d^1); }else { p=s(p,d);q=s(q,d); } } ps=t[q].pos; return ret; }
int main() { red(n);red(K); build(); for(int i=1;i<=n;i++) { red(a[i]); a[i]^=a[i-1]; insert(root[i-1],root[i]=newnode(i),a[i],i); } priority_queue<rec> q; for(int i=1;i<=n;i++) { int ps; ui tmp=query(0,i,a[i],ps); q.push(rec(i,0,i,ps,tmp)); } ll ans=0; while(K--) { rec now=q.top();q.pop(); ans+=(ll)now.val; int ps;ui tmp; if(now.L<now.ps) { tmp=query(now.L,now.ps-1,a[now.id],ps); q.push(rec(now.id,now.L,now.ps-1,ps,tmp)); } if(now.ps<now.R) { tmp=query(now.ps+1,now.R,a[now.id],ps); q.push(rec(now.id,now.ps+1,now.R,ps,tmp)); } } printf("%lld\n",ans); return 0; }
|