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;
}

 

posted @ 2019-04-26 21:24  lleozhang  Views(174)  Comments(0Edit  收藏  举报
levels of contents