jzoj4598. 【NOIP2016模拟7.9】准备食物

一个th的题(a gensokyo)

难度系数在该知识点下为$2.1$

区间xor我们很明显会想到trie树,将每一个区间$l~r$异或和拆成$sum[l-1]$ $sum[r]$两个数的异或

注意到二进制的性质,比当前低的位即使都取1加起来都没有这位选1答案高,所以考虑贪心

题目中每次查询的范围都被限定在一个区间,所以考虑弄出了“$1~r$范围中所有前缀异或和的trie”后怎么搞

考虑每一位,当这一位的某一个儿子在$s[r]$与当前trie节点xor为1,就设这个儿子为”大儿子”,否则是“小儿子”,分别记为$a$,$b$

记$ed[x]$表示在插入以后,$x$这个节点代表的数出现个数,$siz[x]$表示$x$节点子树的所有节点$ed$值的和

根据异或的性质,如果$k$在当前考虑的这一位值为0,那么就将答案加上$sz[b]$(因为b所有子树代表值都比$k$大),然后考虑下一位

否则,不累加答案,考虑下一位

当位数超出限制,返回当前节点$ed$值,当现在树为空,返回$0$

于是本题成功解决

#include<bits/stdc++.h>
using namespace std;
#define N 100010
typedef unsigned int ui;
ui n,k,a[N],ct,nc,sz[N<<6],tw[N],s[N];
int c[N<<6][2],d[N][35],rt[N],qq;
void ins(int p,int &o,int x,int t){
    if(!o)o=++nc;
    sz[o]=sz[p]+1;
    if(x>32)return;
    c[o][!d[t][x]]=c[p][!d[t][x]];
    ins(c[p][d[t][x]],c[o][d[t][x]],x+1,t);
}
ui q(int o,int k,int x,int t){
    if(x>32||!o)return sz[o];
    int a=c[o][d[t][x]],b=c[o][!d[t][x]],p=k&tw[32-x];
    if(!p)return q(a,k,x+1,t)+sz[b];
    return q(b,k,x+1,t);
}
int main(){
    freopen("food.in","r",stdin);
    freopen("food.out","w",stdout);
    scanf("%u",&n);
    tw[0]=1;
    for(int i=1;i<=32;i++)
        tw[i]=tw[i-1]*2ll;
    for(int i=1;i<=n;i++){
        scanf("%u",&a[i]);
        s[i]=s[i-1]^a[i];
    }
    for(int i=1;i<=n;i++){
        ui x=s[i];
        ct=0;
        while(x){
            d[i][++ct]=x&1;
            x/=2;
        }
        reverse(d[i]+1,d[i]+33);
        ins(rt[i-1],rt[i],1,i-1);
    }
    scanf("%d",&qq);
    while(qq--){
        int r,k;
        scanf("%d%d",&r,&k);
        printf("%u\n",q(rt[r],k,1,r));
    }
}

 

转载于:https://www.cnblogs.com/rilisoft/p/10963769.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值