BZOJ 4571: [Scoi2016]美味(权值线段树查询最大异或和)
2018-03-26 15:34
453 查看
题目大意
给定一个序列a1~an给定若干个询问, b , x , L , R
最大化b xor (ai+x)|L<=i<=R
分析
不考虑x这个偏移量我们用二进制trie现在考虑了有一个新操作就是权值线段树
把ai全部丢到权值线段树里面
假设我们所有的数二进制长度不超过5
假设我们贪心地选择了待选择的ai的前两位是10(之前选择的时候我们可以保证有这样的a存在)
考虑当前如何贪心,b的第三位是1,根据贪心我们希望a的第三位是0,那么我们就查询是否存在a的值属于[10000,10011](前两位不变,第三位为0),存在我们就把a的第三位选择为0即可,否则只有选择1。
其中b的第三位为0的时候操作差不多。
区间查询的时候加上变量就好了
注意
二进制是从0开始的,但这并不意味着你应该在高位-1比如这里的极限是2^17
那么你就该从第17位一直考虑到0,不要妄图从16开始考虑
还有就是17位全部为1的话要用(1<<18)-1
坑了我好久啊啊啊啊啊啊啊啊啊啊啊啊
还有就是要判断空区间的情况,以及最好保证查询的区间被包含于(l,r),虽然好像只要有交集就可以弄对,但是容易出细节问题哎。
代码
#include<bits/stdc++.h> using namespace std; const int maxn=2e5+105,oo=17,N=(1<<oo)-1; struct SegmentTree{ int np,rt[maxn],lc[maxn*22],rc[maxn*22],sz[maxn*22]; void Initial() { np=0; memset(rt,0,sizeof(rt)); memset(lc,0,sizeof(lc)); memset(rc,0,sizeof(rc)); } void pushup(int now) { sz[now]=sz[lc[now]]+sz[rc[now]]; } void Build(int &now,int L,int R) { now=++np; if(L==R) { sz[now]=0; return; } int m=(L+R)>>1; Build(lc[now],L,m); Build(rc[now],m+1,R); pushup(now); } int Newnode(int now) { ++np; lc[np]=lc[now];rc[np]=rc[now];sz[np]=sz[now]; return np; } void modify(int pre,int &now,int L,int R,int i) { now=Newnode(pre); if(L==R) { sz[now]++; return; } int m=(L+R)>>1; if(i<=m) modify(lc[pre],lc[now],L,m,i); else modify(rc[pre],rc[now],m+1,R,i); pushup(now); } int query(int now1,int now2,int L,int R,int i,int j)//[i,j]的范围大一点没有关系,只要和[l,R]有交集即可 { if(i<=L && R<=j) return sz[now2]-sz[now1]; int m=(L+R)>>1,ret=0; if(i<=m)ret+=query(lc[now1],lc[now2],L,m,i,j); if(j>m) ret+=query(rc[now1],rc[now2],m+1,R,i,j); return ret; } }sgt; int n,m; void Init() { int a; sgt.Initial(); sgt.Build(sgt.rt[0],0,N); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a); sgt.modify(sgt.rt[i-1],sgt.rt[i],0,N,a); } } int Q,x,l,r; int DFS(int a,int b,int i)//这里是返回a+x的最优值 { if(a==b)return a; int mid=(a+b)>>1; if( Q & (1<<i) )//第i位为1,那么我们希望a为0 { if(max(0,a-x)<=min(N,mid-x) && sgt.query(sgt.rt[l-1],sgt.rt[r],0,N,max(0,a-x),min(N,mid-x) ) ) return DFS(a,mid,i-1); else return DFS(mid+1,b,i-1); } else { if(max(0,mid+1-x)<=min(N,b-x) && sgt.query(sgt.rt[l-1],sgt.rt[r],0,N,max(0,mid+1-x),min(N,b-x) ) ) return DFS(mid+1,b,i-1); else return DFS(a,mid,i-1); } } int main() { //freopen("in.txt","r",stdin); //freopen("out1.txt","w",stdout); Init(); while(m--) { scanf("%d%d%d%d",&Q,&x,&l,&r); printf("%d\n",Q^DFS(0, ( 1<<(oo+1) )-1 , oo ) ); } return 0; }
相关文章推荐
- 【bzoj4571: [Scoi2016]美味】区间异或和最大 ,可持久化线段树(主席树)
- bzoj 4571: [Scoi2016]美味
- BZOJ 4571: [Scoi2016]美味
- [BZOJ 4571][scoi2016]美味 主席树
- BZOJ.4571.[SCOI2016]美味(主席树 贪心)
- BZOJ 4571 【SCOI2016】 美味
- bzoj 4571 [Scoi2016]美味
- 【BZOJ 4571】美味 【区间异或最大值】【主席树】【贪心】
- [主席树] BZOJ 4571 [Scoi2016]美味
- BZOJ 4571: [Scoi2016]美味 主席树
- BZOJ 4571 [Scoi2016] 美味
- BZOJ 4571: [Scoi2016]美味
- 【BZOJ 4571】【SCOI 2016】美味
- BZOJ 1858: [Scoi2010]序列操作 线段树区间修改查询
- 【BZOJ3110】K大数查询(权值线段树套线段树+标记永久化,整体二分)
- 【BZOJ3110】【codevs1616】K大数查询,权值线段树套普通线段树
- 4571: [Scoi2016]美味
- [BZOJ] 1012 - [JSOI2008] - 最大数maxnumber - 线段树 - 单点更新 - 区间查询最大
- 【bzoj4719】[Noip2016]天天爱跑步 权值线段树合并
- 【bzoj3110】[Zjoi2013]K大数查询 权值线段树套区间线段树