十二省联考D1T1 异或粽子【可持久化Trie+优先队列】

洛谷传送门

题目分析:

我们知道Trie树可以对某一个值查询与它异或第k大的值。
但是全局前k大就懵逼了。
这时候就有一个神奇的技巧套路,对每个右端点,找到与它异或最大的左端点,将这n个值放入优先队列中。
这时候队首一定是全局最大值,加进答案中,pop掉,然后再找到这个值对应的右端点的左端点中第二大的,放入优先队列中。
这时候的队首一定是全局第二大。
一直这样做k次即可。
找右端点对应的第几大左端点就用可持久化Trie,可以每次删除然后找最大,也可以每次找第i大,弹出一次下一次就找第i+1大。

1<<32位见祖宗系列(考场爆零选手已无力吐槽恶心数据范围)。。。
要是我以后到了清华一定找到出题人把他挂起来裱。。。
这题还是BZOJ原题。。。 BZOJ3689异或之

Code:

#include<cstdio>
#include<cctype>
#include<queue>
#define maxn 500005
#define maxp maxn*64
using namespace std;
template<class T>inline void read(T &a){
    char c;while(!isdigit(c=getchar()));
    for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
typedef unsigned int ui;
int n,k,rt[maxn],ch[maxp][2],siz[maxp],tot;
ui a[maxn];
struct node{
    ui x; int pos;
    bool operator < (const node &p)const{return x<p.x;}
};
priority_queue<node>q;
void insert(int &now,int i,ui x,int d){
    siz[++tot]=siz[now]+d,ch[tot][0]=ch[now][0],ch[tot][1]=ch[now][1];
    now=tot;
    if(i==-1) return;
    insert(ch[now][x>>i&1],i-1,x,d);
}
ui find(int now,ui x){
    ui ret=0;
    for(int i=31,v;i>=0;i--,now=ch[now][v]){
        v=!(x>>i&1);
        if(siz[ch[now][v]]) ret|=1u<<i;
        else v=!v;
    }
    return ret;
}
int main()
{
    read(n),read(k);
    insert(rt[0],31,0,1);
    for(int i=1;i<=n;i++) 
		read(a[i]),a[i]^=a[i-1],
		q.push((node){find(rt[i-1],a[i]),i}),insert(rt[i]=rt[i-1],31,a[i],1);
    long long ans=0; int x; ui y,tmp;
    while(k--){
        ans+=(tmp=q.top().x),x=q.top().pos,y=0;q.pop();
        for(int i=31;i>=0;i--) y|=(a[x]&(1u<<i))^(tmp&1u<<i);
        insert(rt[x-1],31,y,-1);
        if(siz[rt[x-1]]) q.push((node){find(rt[x-1],a[x]),x});
    }
    printf("%lld",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值