BZOJ4571: [Scoi2016]美味
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]$的数字。
这个区间范围限制,我们直接线段树就好了。
那,那个外层区间范围限制怎么整?
我们不是根据前一位推出了当前这一位嘛。。。
那就用个主席树来维护一下嘛。。。
主席树直接上板子。。。
然后就完结了。。。
附代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #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; } |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步