准备食物(trie) 题解+代码

44 篇文章 0 订阅
5 篇文章 0 订阅

Description

“~妖梦,我又饿了!”
魂魄妖梦身为西行寺家的专属庭师第二代兼大小姐的西行寺幽幽子的护卫,却承担了为幽幽子准备食物的任务。幽幽子是个非常贪吃的亡灵,所以妖梦经常为食物的问题所困。
现在,妖梦有n盘食物排成一排,第i盘食物有一个属性a[i]。亡灵的体质比较特殊,所以妖梦认为食物的属性很重要。妖梦会进行q次询问,每次给出两个整数r,k,她想知道有多少个区间[i,r](1≤i≤r),区间内所有食物属性值的异或大于等于k。

Input

第一行一个整数n,接下来n个非负整数,表示每盘食物的属性。
接下来一行,一个整数q,表示询问个数。接下来q行,每行两个整数r,k,意义如题目所述。

Output

q行,每行一个整数表示该询问的答案。

Sample Input

4
1 2 3 4
3
2 2
4 5
3 2

Sample Output

2
2
1

Data Constraint

30%:n,q≤1000
另有30%数据:n≤1000且询问的r是不下降的
100%:1≤n,q≤100000 r≤n 0≤k,a[i]≤1,000,000,000

Solution

对于30%,直接枚举n,判断是否合法,复杂度 O(nq)
然而,由于OJ跑的比较快,另外30分也顺便过了。
对于100%
发现n和q比较大,a[i]也很大,考虑使用trie树离线操作
设一个x[i]表示a[1]^a[2]^a[3]^……^a[i],从1向右枚举,每次把x[i]加入trie,遇到查询就把x[r]与trie之前的查询判断大于等于k的有多少个,然后,就没有然后了。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 101000
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int n,m,a[N],t[10000000][3],tot=0,c[31],d[31],ans[N];
struct note{
    int r,k,s;
};
note q[N];
bool cnt(note x,note y){return x.r<y.r;}
void put(int x)
{
    memset(c,0,sizeof(c));int j=31;
    for(int k=x;k;k/=2) c[--j]=k%2;
    int ls=0;
    fo(i,1,30)
    {
        int y=c[i];
        if (t[ls][y]==0){
            t[ls][y]=++tot;ls=tot;t[ls][2]++;
        }
        else{
            ls=t[ls][y];t[ls][2]++;
        }
    }
}
int get(int x,int v,int y)
{
    if (v==0) return 0;
    if (c[x]^y==0)
    {
        if (d[x]==1) return t[v][2];
    }
    else
    {
        if(d[x]==0) return 0;
    }
    return get(x+1,t[v][0],0)+get(x+1,t[v][1],1);
}
int main()
{
    freopen("food.in","r",stdin);
    freopen("food.out","w",stdout);
    scanf("%d",&n);fo(i,1,n) scanf("%d",&a[i]);
    scanf("%d",&m);
    fo(i,1,m) scanf("%d%d",&q[i].r,&q[i].k),q[i].s=i;
    sort(q+1,q+m+1,cnt);
    int j=1,jy=0;
    fo(i,1,n)
    {
        jy^=a[i];
        while (i==q[j].r) {
            memset(d,0,sizeof(d));int l=31;
            for(int k=q[j].k;k;k/=2) d[--l]=k%2;
            memset(c,0,sizeof(c));l=31;
            for(int k=jy;k;k/=2) c[--l]=k%2;
            ans[q[j].s]=q[j].r-get(1,t[0][0],0)-get(1,t[0][1],1);
            if (jy<q[j].k) ans[q[j].s]--;
            j++;
        }
        put(jy);
    }
    fo(i,1,m) printf("%d\n",ans[i]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值