bzoj 2741
题目描述:这里
一道非常好的题
由于强制在线,我们必须要用一些数据结构来处理
考虑分块:将整个序列分块,块内部分预处理,块外部分暴力处理
对于每个块,计算出以这个块的左端点为端点,向右枚举这个块以后的所有点,然后记录下这样一个区间的最大异或值
然后每次查询的时候直接调用即可
#include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> #define ll long long using namespace std; struct Trie { int to[2]; int ed; }tree[20000005]; struct node { ll v; int rq; int num; friend bool operator < (node a,node b) { return a.v<b.v; } }; priority_queue <node> M; int rt[500005]; ll a[500005]; int n,k; int tot=0; void ins(ll x,int now,int las) { rt[now]=++tot; now=rt[now],las=rt[las]; for(int i=32;i>=0;i--) { tree[now]=tree[las]; tree[now].ed++; if((x>>i)&1)tree[now].to[1]=++tot,now=tree[now].to[1],las=tree[las].to[1]; else tree[now].to[0]=++tot,now=tree[now].to[0],las=tree[las].to[0]; } tree[now].ed=tree[las].ed+1; } ll query(int lq,int rq,ll x,int rk,int temp) { if(temp==-1)return 0; int t=((x>>temp)&1)?0:1; int sum=tree[tree[rq].to[t]].ed-tree[tree[lq].to[t]].ed; if(sum>=rk)return query(tree[lq].to[t],tree[rq].to[t],x,rk,temp-1)+(1ll<<temp); else return query(tree[lq].to[t^1],tree[rq].to[t^1],x,rk-sum,temp-1); } int main() { scanf("%d%d",&n,&k); n++; ins(0,1,0); for(int i=2;i<=n;i++)scanf("%lld",&a[i]),a[i]^=a[i-1],ins(a[i],i,i-1); for(int i=2;i<=n;i++)M.push((node){query(rt[0],rt[i],a[i],1,32),i,1}); ll ans=0; for(int i=1;i<=k;i++) { node temp=M.top(); M.pop(); ans+=1ll*temp.v; if(temp.num<temp.rq)M.push((node){query(rt[0],rt[temp.rq],a[temp.rq],temp.num+1,32),temp.rq,temp.num+1}); } printf("%lld\n",ans); return 0; }