【bzoj4571&&SCOI2016美味】
4571: [Scoi2016]美味
Time Limit: 30 Sec Memory Limit: 256 MB
Submit: 656 Solved: 350
[Submit][Status][Discuss]
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 4 1 4
2 3 2 3
3 2 3 3
4 1 2 4
Sample Output
9
7
6
7
三步走
●維護xor最大值的話用trie樹可以搞一搞
●維護區間xor最大值可以用可持久化trie樹
●維護區間xor並支持加減的就是可持久化權值線段樹了,這就是本題的算法,用貪心+主席樹實現
貪心?肯定先確定最高位使得xor後此位為1,由此得到
策略1:從二進制高位向低位貪心,盡量滿足 b的i位與(a[j]+x)的i位不同
怎麽判斷第i位是否可以不同?可以假設(a[j]+x)的i位與b的i位不同,來確定所選的數的第i位,再判定假設是否合法。
合法的條件是什麽?記已確定的數為ans,只用查找區間內是否存在數這樣的滿足條件
1.前面位和ans相同
2.此位滿足已確定的數
不太容易懂,舉一個例子:
假設b的當前位為1,我們應盡量使a[j]+x當前位為0,前面位確定為ans,ans第i位設定為0,查找區間內是否存在 在[ans,ans+(1<<i)-1]之內的數,如果有,則此位可以為0
判斷區間的數的個數,權值線段樹
但此時我們註意到查找是雙關鍵字的,一個是區間一個是權值,所以用主席樹來完成操作
總結得出
策略2:用主席樹判斷區間數的存在性問題來確定當前位究竟是什麽
到了這裏,此題也就差不多了,但有幾個細節需要註意
●主席樹空間開夠(我就是這裏錯了好久--為什麽dev不報RE而是亂搞數組啊!!)
●由於我們查詢的數是a[j]+x,而樹中插入的是a[],所以查詢區間統一減去x並考慮區間是否超界
●最後註意,我們的到的ans是選出來的數,所以輸出時要xor b
沒有了-------------------------------------------------------------------
#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #define ll long long #define N 200005 #define mx (1<<18)-1 using namespace std; int n,m,a[N],rt[N],ls[N<<5],rs[N<<5],sum[N<<5],sz; void insert(int pre,int &u,int l,int r,int pos){ u=++sz;ls[u]=ls[pre];rs[u]=rs[pre];sum[u]=sum[pre]+1; if(l==r)return;int mid=(l+r)>>1; if(pos<=mid)insert(ls[pre],ls[u],l,mid,pos); else insert(rs[pre],rs[u],mid+1,r,pos); } int query(int pre,int u,int l,int r,int L,int R){ if(L<=l&&r<=R)return sum[u]-sum[pre]; int mid=(l+r)>>1;int t=0; if(L<=mid)t+=query(ls[pre],ls[u],l,mid,L,R); if(R>mid)t+=query(rs[pre],rs[u],mid+1,r,L,R); return t; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=n;i++) insert(rt[i-1],rt[i],0,mx,a[i]); for(int i=1;i<=m;i++){ int b,x,l,r; scanf("%d%d%d%d",&b,&x,&l,&r); int ans=0; for(int j=17;j>=0;j--){ if(b&(1<<j)){ int L=max(ans-x,0),R=ans+(1<<j)-x-1; if(R<0||!query(rt[l-1],rt[r],0,mx,L,R))ans^=(1<<j); } else{ ans^=(1<<j); int L=max(ans-x,0),R=ans+(1<<j)-x-1; if(R<0||!query(rt[l-1],rt[r],0,mx,L,R))ans^=(1<<j); } } printf("%d\n",ans^b); } return 0; }
【bzoj4571&&SCOI2016美味】