BZOJ4517[Scoi2016]美味——主席树
题目描述
一家餐厅有 n 道菜,编号 1...n ,大家对第 i 道菜的评价值为 ai(1≤i≤n)。有 m 位顾客,第 i 位顾客的期
望值为 bi,而他的偏好值为 xi 。因此,第 i 位顾客认为第 j 道菜的美味度为 bi XOR (aj+xi),XOR 表示异或
运算。第 i 位顾客希望从这些菜中挑出他认为最美味的菜,即美味值最大的菜,但由于价格等因素,他只能从第
li 道到第 ri 道中选择。请你帮助他们找出最美味的菜。
输入
第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
输出
输出 m 行,每行 1 个整数,ymax ,表示该位顾客选择的最美味的菜的美味值。
样例输入
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
样例输出
9
7
6
7
7
6
7
一看到位异或第一时间就会想到按位从高到底贪心,这道题也是这样的,但稍有不同的是要两个数的和与另一个数位异或结果最大。还是从高位到低位考虑,如果b当前为是1(0的方法一样),那么希望x+a这一位是0,因为更高位已经确定了,所以满足要求的a+x的值一定是一个区间——假设更高位已经确定的答案是ans(ans的值即为更高位贪心取最优解,其他位都是0),那么a+x的区间就是[ans,ans+(1<<i)-1],把x挪到等号那边就能知道要使这一位异或结果最优所需的a的范围,只要在主席树上查询是否有这个区间内的数,有的话把ans的这一位加上b这一位相反的数。因为得到的ans是a+x的值,所以最后还要ans^=b。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n,m; int l,r; int cnt; int ans; int maxv; int a,b,x; int ls[4000010]; int rs[4000010]; int sum[4000010]; int root[200010]; int updata(int pre,int l,int r,int v) { int rt=++cnt; if(l==r) { sum[rt]=sum[pre]+1; return rt; } ls[rt]=ls[pre]; rs[rt]=rs[pre]; sum[rt]=sum[pre]+1; int mid=(l+r)>>1; if(v<=mid) { ls[rt]=updata(ls[pre],l,mid,v); } else { rs[rt]=updata(rs[pre],mid+1,r,v); } return rt; } int query(int x,int y,int l,int r,int L,int R) { if(L<=l&&r<=R) { return sum[y]-sum[x]; } int mid=(l+r)>>1; if(L>mid) { return query(rs[x],rs[y],mid+1,r,L,R); } else if(R<=mid) { return query(ls[x],ls[y],l,mid,L,R); } else { return query(ls[x],ls[y],l,mid,L,R)+query(rs[x],rs[y],mid+1,r,L,R); } } bool find(int l,int r,int L,int R) { L=max(0,L); R=min(maxv,R); if(L>R) { return 0; } else { return query(root[l],root[r],0,1<<18,L,R); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a); maxv=max(a,maxv); root[i]=updata(root[i-1],0,1<<18,a); } for(int j=1;j<=m;j++) { scanf("%d%d%d%d",&b,&x,&l,&r); ans=0; for(int i=17;i>=0;i--) { int now=ans+((((b>>i)&1)^1)<<i); if(find(l-1,r,now-x,now+(1<<i)-1-x)) { ans=now; } else { ans+=(((b>>i)&1)<<i); } } printf("%d\n",ans^b); } }