【洛谷】P3293 [SCOI2016]美味(贪心+可持久化线段树)
作者:互联网
题意
一家餐厅有 \(n\) 道菜,编号 \(1,2,...,n\) ,大家对第 \(i\) 道菜的评价值为 \(a_i\)。有 \(m\) 位顾客,第 \(i\) 位顾客的期望值为 \(b_i\),而他的偏好值为 \(x_i\)。因此,第 \(i\) 位顾客认为第 \(j\) 道菜的美味度为 \(b_i\,\,xor\,\, (a_j+x_i)\),\(xor\) 表示异或运算。
第 \(i\) 位顾客希望从这些菜中挑出他认为最美味的菜,即美味值最大的菜,但由于价格等因素,他只能从第 \(l_i\) 道到第 \(r_i\) 道中选择。请你帮助他们找出最美味的菜。
数据范围
\(1 \le n \le 2 \times 10^5\),\(0 \le a_i,b_i,x_i < 10^5\),\(1 \le l_i \le r_i \le n\)(\(1 \le i \le m\)),\(1 \le m \le 10^5\)。
思路
由于是求异或的最大值,可以考虑按二进制位从高到底贪心地枚举。
假设当前枚举到第 \(i\) 位,已经枚举过的位数上的答案为 \(val\),\(b\) 的第 \(i\) 位为 1(为 0 时同理),那么此时我们就想要在 \(a[l] \sim a[r]\) 中查询一个数,满足其加上 \(x\) 后前 \(i-1\) 位为 \(val\),且第 \(i\) 位为 0。可以推出这样的数在区间 \([val-x,val+(1<<i)-1-x]\) 中。
而需要在给定的区间中寻找特定的值,可以用主席树维护序列 \(a\)。具体实现可以看代码。
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+10;
int n,m,tot,a[N],root[N],w;
struct tree{int ls,rs,cnt;}tr[N*21];
void insert(int p,int &q,int l,int r,int val)
{
q=++tot;if(l==r) { tr[q].cnt=tr[p].cnt+1;return ;}int mid=l+r>>1;
tr[q]=tr[p];tr[q].cnt++;
if(val<=mid) insert(tr[p].ls,tr[q].ls,l,mid,val);
else insert(tr[p].rs,tr[q].rs,mid+1,r,val);
}
int query(int p,int q,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr) return tr[q].cnt-tr[p].cnt;
int res=0,mid=l+r>>1;
if(ql<=mid) res+=query(tr[p].ls,tr[q].ls,l,mid,ql,qr);
if(qr>mid) res+=query(tr[p].rs,tr[q].rs,mid+1,r,ql,qr);
return res;
}
bool check(int p,int q,int ql,int qr)
{
ql=max(ql,0);qr=min(qr,w);
if(ql>qr) return false;
return query(root[p],root[q],0,w,ql,qr);
}
int main()
{
scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%d",&a[i]),w=max(w,a[i]);
for(int i=1;i<=n;i++) insert(root[i-1],root[i],0,w,a[i]);
while(m--)
{
int b,x,l,r,val=0;scanf("%d%d%d%d",&b,&x,&l,&r);
for(int i=17;i>=0;i--)
{
int tmp=val+((1^((b>>i)&1))<<i);
if(check(l-1,r,tmp-x,tmp+(1<<i)-1-x)) val=tmp;
else val+=(((b>>i)&1)<<i);
}
printf("%d\n",val^b);
}
return 0;
}
标签:le,洛谷,val,int,tr,P3293,qr,SCOI2016,ql 来源: https://www.cnblogs.com/NLCAKIOI/p/16467535.html