bzoj4571: [Scoi2016]美味【主席树】

Description

一家餐厅有 n 道菜,编号 1…n ,大家对第 i 道菜的评价值为 ai(1≤i≤n)。有 m 位顾客,第 i 位顾客的期
望值为 bi,而他的偏好值为 xi 。因此,第 i 位顾客认为第 j 道菜的美味度为 bi XOR (aj+xi),XOR 表示异或
运算。第 i 位顾客希望从这些菜中挑出他认为最美味的菜,即美味值最大的菜,但由于价格等因素,他只能从第
li 道到第 ri 道中选择。请你帮助他们找出最美味的菜。

Input

第1行,两个整数,n,m,表示菜品数和顾客数。
第2行,n个整数,a1,a2,…,an,表示每道菜的评价值。
第3至m+2行,每行4个整数,b,x,l,r,表示该位顾客的期望值,偏好值,和可以选择菜品区间。
1≤n≤2×10^5,0≤ai,bi,xi<10^5,1≤li≤ri≤n(1≤i≤m);1≤m≤10^5

Output

输出 m 行,每行 1 个整数,ymax ,表示该位顾客选择的最美味的菜的美味值。

Sample Input

4 4

1 2 3 4

1 4 1 4

2 3 2 3

3 2 3 3

4 1 2 4

Sample Output

9

7

6

7

解题思路:

如果只是求bi XOR aj 的最大值,只用建可持久化trie树,但现在要加x,所以不太行。
考虑把trie数建成主席树,就只用把权值区间改为2的整数次幂,那么(l,mid),(mid+1,r)就对应了该位为0或1,如果加上x,就是(l-x,mid-x),(mid+1-x,r-x),所以每一位要独立查询,时间复杂度为 O(nlog2n) O ( n l o g 2 n )

#include<bits/stdc++.h>
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();c!='-'&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=200005,M=5000005;
int n,m,mx,tot,L,R,b,v,a[N],rt[N];
struct node{int lc,rc,size;}tr[M];
void insert(int y,int &x,int l,int r,int val)
{
    tr[x=++tot]=tr[y],tr[x].size++;
    if(l==r)return;
    int mid=l+r>>1;
    if(val<=mid)insert(tr[y].lc,tr[x].lc,l,mid,val);
    else insert(tr[y].rc,tr[x].rc,mid+1,r,val);
}
int query_size(int x,int y,int l,int r,int ql,int qr)
{
    if(r<0)return 0;l=max(l,0);
    if(ql<=l&&r<=qr)return tr[y].size-tr[x].size;
    int mid=l+r>>1,res=0;
    if(ql<=mid)res+=query_size(tr[x].lc,tr[y].lc,l,mid,ql,qr);
    if(qr>mid)res+=query_size(tr[x].rc,tr[y].rc,mid+1,r,ql,qr);
    return res;
}
int query_mx(int x,int y,int l,int r,int res,int bin)
{
    if(bin==-1)return res;
    int mid=l+r>>1,t=(b>>bin)&1;
    if(t)
    {
        int size=query_size(rt[L-1],rt[R],0,mx,l-v,mid-v);
        if(size)return query_mx(tr[x].lc,tr[y].lc,l,mid,res|(1<<bin),bin-1);
        else return query_mx(tr[x].rc,tr[y].rc,mid+1,r,res,bin-1);
    }
    else
    {
        int size=query_size(rt[L-1],rt[R],0,mx,mid+1-v,r-v);
        if(size)return query_mx(tr[x].rc,tr[y].rc,mid+1,r,res|(1<<bin),bin-1);
        else return query_mx(tr[x].lc,tr[y].lc,l,mid,res,bin-1);
    }
}
int main()
{
    //freopen("lx.in","r",stdin);
    n=getint(),m=getint();
    for(int i=1;i<=n;i++)a[i]=getint();
    int bin=18;mx=(1<<bin)-1;
    for(int i=1;i<=n;i++)insert(rt[i-1],rt[i],0,mx,a[i]);
    while(m--)
    {
        b=getint(),v=getint();L=getint(),R=getint();
        printf("%d\n",query_mx(rt[L-1],rt[R],0,mx,0,bin-1));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值