Description
给出一个长度为n的序列,m询问,每次询问求出[l,r]范围内的每一个数加上x再与b异或能够得到的最大值。
Input
第1行,两个整数,n,m,表示菜品数和顾客数。
第2行,n个整数,a1,a2,…,an,表示每道菜的评价值。
第3至m+2行,每行4个整数,b,x,l,r,表示该位顾客的期望值,偏好值,和可以选择菜品区间。
1
≤
n
≤
2
×
1
0
5
,
0
≤
a
i
,
b
i
,
x
i
<
1
0
5
,
1
≤
l
i
≤
r
i
≤
n
(
1
≤
i
≤
m
)
;
1
≤
m
≤
1
0
5
1≤n≤2×10^5,0≤ai,bi,xi<10^5,1≤li≤ri≤n(1≤i≤m);1≤m≤10^5
1≤n≤2×105,0≤ai,bi,xi<105,1≤li≤ri≤n(1≤i≤m);1≤m≤105
Output
输出 m 行,每行 1 个整数,ymax ,表示该位顾客选择的最美味的菜的美味值。
Solution
我们把b转换成二进制。
假设二进制下是
b
k
b
k
−
1
.
.
.
b
0
b_kb_{k-1}...b_0
bkbk−1...b0
答案肯定<=100000 有18位(17~0)
我们从高位往低位贪心取,每次判断答案的这一位能不能取1.
假设我们处理到第i+1位,答案和为sum,要处理第i位。
那么新的ans=
∈
\in
∈[
s
u
m
+
2
i
,
s
u
m
+
2
i
+
1
−
1
sum+2^i,sum+2^{i+1}-1
sum+2i,sum+2i+1−1]
而这个能不能取到可以在权值线段树找。
[l,r]区间限制动态开点建版本差分就好了。
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node {
int ls,rs,siz;
} tree[6000000];
int rt[200010],cnt;
int n,m,a[220010];
void update(int &id,int pre,int l,int r,int d,int v) {
id=++cnt;
tree[id]=tree[pre];
tree[id].siz++;
if(l==r){
tree[id].siz+=v;
return;
}
int mid=(l+r)/2;
if(d<=mid) update(tree[id].ls,tree[pre].ls,l,mid,d,v);
else update(tree[id].rs,tree[pre].rs,mid+1,r,d,v);
}
bool query(int id,int pre,int l,int r,int x,int y) {
if(x<=l&&y>=r) return tree[id].siz-tree[pre].siz;
if(tree[id].siz-tree[pre].siz==0) return 0;
int mid=(l+r)/2,res=0;
if(x<=mid) res|=query(tree[id].ls,tree[pre].ls,l,mid,x,y);
if(res) return 1;
if(y>mid) res|=query(tree[id].rs,tree[pre].rs,mid+1,r,x,y);
return res;
}
int main(){
int u,v,x,b;
scanf("%d%d",&n,&m);
for(int i=1;i<=n; i++) {
scanf("%d",&a[i]);
update(rt[i],rt[i-1],0,(1<<18)-1,a[i],1);
}
int mid,l,r;
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&b,&x,&u,&v);
l=0,r=(1<<18)-1;
for(int j=17;j>=0;j--){
mid=(l+r)/2;
if(b&(1<<j))
if(query(rt[v],rt[u-1],0,(1<<18)-1,max(0,l-x),max(0,mid-x))) r=mid;
else l=mid+1;
else {
if(query(rt[v],rt[u-1],0,(1<<18)-1,max(mid-x+1,0),max(r-x,0))) l=mid+1;
else r=mid;
}
}
printf("%d\n",l^b);
}
}