题解 P3293 【[SCOI2016]美味】

3493441984zz

2019-03-08 10:27:44

Solution

# 主席树 感觉其他关于主席树题解不是很好懂?那我来一发 ------------ # 思路: 首先,我们看到要选区间的数,并且结合题意就知道用主席树啦~~(看题目标签也阔以QAQ)~~ 然后我们看到要求最大异或和,因为异或中,二进制每一位都是独立的,所以我们考虑按位处理 而题目是给定两个数$b,x$,要求$(a[i]+x)\oplus b$,那么既然$b,x$给定了,我们只需要维护一下$a[i]$就行了 于是,我们开一颗主席树,以$a[i]$为下标,里面存的值表示出现次数 - 那么我们考虑下面的情况: 首先,我们根据贪心思路,我们从高到低一位一位来看,显然如果可以让当前这一位为$1$,那么肯定会更优(不管对后面的影响) 那么我们分两类讨论($ans$记录的是$a[i]+x$,主席树如上文所述): - 假设现在我们处理到了第$i$位,如果此时$b$这一位为$0$,那么我们为了最终$ans\oplus b$更大,$ans$最好在这一位为$1$,那么$ans$的范围多少呢? 其实就是$[ans+(1<<i),ans+(1<<(i+1))-1]$ 为什么呢? 举个例子,假设枚举到了第$4$位,最右边为最低位,此时的$ans$为$1010000$,因为只枚举到了第四位,所以第五位之后都是$0$,那么为了让第四位是$1$,那么范围就是$[1011000,1011111]$ 而我们知道了$ans$的范围,而$ans$代表的是$a[i]+x$,那么我们其实只需要知道有没有$a[i]$在$[ans+(1<<i)-x,ans+(1<<(i+1))-1-x]$这个区间就可以了,而题目中限制了在某个区间中,直接上主席树板子查询就$ok$ - 那么如果$b$此时为$1$的话同理,那么我们为了最终$ans\oplus b$更大,$ans$这一位最好为$0$,那么$ans$的取值范围是$[ans,ans+(1<<i)-1]$,自然$a[i]$的取值范围就是$[ans-x,ans+(1<<i)-1-x]$了 最后把$ans$异或一下$b$就是答案啦 ------------ 下面是美滋滋的代码时间~~~ ~~~cpp #include<iostream> #include<cstdio> #include<cstring> #define N 200007 #define M 100007 using namespace std; struct Tree { int ls,rs,sum; }tr[N<<7]; int n,m,cnt; int root[N]; void Pushup(int rt) { tr[rt].sum=tr[tr[rt].ls].sum+tr[tr[rt].rs].sum; } void Build(int &rt,int l,int r) { if(!rt) rt=++cnt; if(l==r) return; int mid=l+((r-l)>>1); Build(tr[rt].ls,l,mid); Build(tr[rt].rs,mid+1,r); } void Insert(int &now,int last,int l,int r,int pos) { if(!now) now=++cnt; if(l==r) { tr[now].sum=tr[last].sum+1; return; } int mid=l+((r-l)>>1); if(pos<=mid) { tr[now].rs=tr[last].rs; Insert(tr[now].ls,tr[last].ls,l,mid,pos); } else { tr[now].ls=tr[last].ls; Insert(tr[now].rs,tr[last].rs,mid+1,r,pos); } Pushup(now); } bool Search(int now,int last,int l,int r,int ql,int qr) { if(tr[last].sum-tr[now].sum<1) return 0; if(ql<=l&&r<=qr) return 1; int mid=l+((r-l)>>1),tot=0; if(ql<=mid) tot+=Search(tr[now].ls,tr[last].ls,l,mid,ql,qr); if(qr>mid) tot+=Search(tr[now].rs,tr[last].rs,mid+1,r,ql,qr); return tot; } int main() { scanf("%d%d",&n,&m); Build(root[0],0,M); for(int i=1;i<=n;++i) { int in; scanf("%d",&in); Insert(root[i],root[i-1],0,M,in); } for(int i=1;i<=m;++i) { int b,x,ql,qr,ans=0; scanf("%d%d%d%d",&b,&x,&ql,&qr); for(int j=17;j>=0;--j) { int L,R,opt; if(b&(1<<j)) L=ans,R=ans+((1<<j)-1),opt=0; else L=ans+(1<<j),R=ans+((1<<(j+1))-1),opt=1; //printf("\n-------test-------\n"),printf("ans:%d",opt),printf("\n-------test-------\n"); if(!Search(root[ql-1],root[qr],0,M,max(L-x,0),min(R-x,M))) opt^=1; ans+=(opt<<j); } printf("%d\n",ans^b); } return 0; } ~~~