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
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
7
6
7
题解Here!
看到异或,就应该想到要用可持久化$Trie$树来搞搞事。
对于这道题,我们需要借鉴一下最大异或和的解题思想。
于是省去了建$Trie$树的部分。
我们想,可持久化$Trie$树的核心思想是贪心,一位一位贪心。
所以我们同样是按照数位一位一位的贪心。
因为加了一个$x$,所以我们考虑对于所有的$a_i+x$与$b$的按位异或。
假设我们已经处理到$b$的二进制第$i$位,假设是这一位上是$1$。
那么我们只需要查找是否存在$a_j+x$使得其二进制第$i$位数字是$0$即可。
由于我们已经处理了前$i-1$位了,那么设前$i-1$位结果是$ans$。
于是我们需要查找的数的大小就是在区间$[ans-x,ans+(1<<i)-1-x]$中。
手算一下就知道这个区间里的数字的第$i$位加了$x$后就都是$0$。
对于$0$同理。
那么现在我们就是要在$a_1,a_2,...,a_n$中找出是否存在于$[ans-x,ans+(1<<i)-1-x]$的数字。
这个区间范围限制,我们直接线段树就好了。
那,那个外层区间范围限制怎么整?
我们不是根据前一位推出了当前这一位嘛。。。
那就用个主席树来维护一下嘛。。。
主席树直接上板子。。。
然后就完结了。。。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 200010
using namespace std;
int n,m,K,size=0;
int val[MAXN],root[MAXN];
struct Chairman_Tree{
int sum,l,r;
}a[MAXN*20];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
inline void buildtree(){
root[0]=a[0].l=a[0].r=a[0].sum=0;
}
void insert(int k,int l,int r,int &rt){
a[++size]=a[rt];rt=size;
a[rt].sum++;
if(l==r)return;
int mid=l+r>>1;
if(k<=mid)insert(k,l,mid,a[rt].l);
else insert(k,mid+1,r,a[rt].r);
}
int query(int l,int r,int lside,int rside,int i,int j){
if(l<=lside&&rside<=r)return a[j].sum-a[i].sum;
int mid=lside+rside>>1,ans=0;
if(l<=mid)ans+=query(l,r,lside,mid,a[i].l,a[j].l);
if(mid<r)ans+=query(l,r,mid+1,rside,a[i].r,a[j].r);
return ans;
}
bool check(int l,int r,int x,int y){
x=max(0,x);y=min(y,K);
if(x>y)return 0;
return query(x,y,0,K,root[l],root[r]);
}
inline int solve(int b,int x,int l,int r){
int ans=0,now;
for(int i=17;i>=0;i--){
now=ans+((1^((b>>i)&1))<<i);
if(check(l-1,r,now-x,now+(1<<i)-x-1))ans=now;
else ans+=((b>>i)&1)<<i;
}
return (ans^b);
}
void work(){
int l,r,x,b;
while(m--){
b=read();x=read();l=read();r=read();
printf("%d\n",solve(b,x,l,r));
}
}
void init(){
n=read();m=read();
K=((MAXN-10)>>1);
for(int i=1;i<=n;i++)val[i]=read();
buildtree();
for(int i=1;i<=n;i++){
root[i]=root[i-1];
insert(val[i],0,K,root[i]);
}
}
int main(){
init();
work();
return 0;
}