luogu P3293 [SCOI2016]美味

传送门

异或最大值应该是要用(trie)树,从高位往低位贪心,虽然这里询问区间的数都要加上(x),但是仍然可以利用这个思想

从高往低位考虑,我们要找一个加上(x)后当前二进制位(j)不等于(b)的当前位的数,假设(b)当前位为0,我们就要现在找个数加上(x)后当前位(j)为1,记之前选出的数为(ans),那么我们就要找一个在([ans+(1<<j)-x,ans+(1<<j)-(1<<j)-1-x])区间内的数;如果(b)当前位为1,那么取值范围为([ans-x,ans-(1<<j)-1-x]).如果有这个数,那么(ans)当前位(j)就可以等于(b)当前位(xor 1),否则就反过来

这个操作可以用主席树查询某区间的数出现次数的操作实现

#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register
#define db double
#define eps (1e-7)

using namespace std;
const int N=500000+10;
const LL inf=1ll<<45;
il LL rd()
{
  re LL x=0,w=1;re char ch=0;
  while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
  while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
  return x*w;
}

#define mid ((l+r)>>1)

int s[N*20],ch[N*20][2],rt[N],tt;
int n,m,kk,L,R,a[N];
il void inst(int p)
{
  rt[p]=++tt;
  int o1=rt[p],o2=rt[p-1],l=0,r=n;
  s[o1]=s[o2]+1;
  do
    {
      if(a[p]<=mid)
    {
      ch[o1][0]=++tt,ch[o1][1]=ch[o2][1];
      o1=ch[o1][0],o2=ch[o2][0],r=mid;
    }
      else
    {
      ch[o1][0]=ch[o2][0],ch[o1][1]=++tt;
      o1=ch[o1][1],o2=ch[o2][1],l=mid+1;
    }
      s[o1]=s[o2]+1;
    }
  while(l<r);
}
int quer(int o1,int o2,int l,int r,int ll,int rr)
{
  if(ll<=l&&r<=rr) return s[o1]-s[o2];
  int an=0;
  if(ll<=mid) an+=quer(ch[o1][0],ch[o2][0],l,mid,ll,rr);
  if(rr>mid) an+=quer(ch[o1][1],ch[o2][1],mid+1,r,ll,rr);
  return an;
}
bool ok(int l,int r,int ll,int rr)
{
  ll=max(ll,0),rr=min(rr,n);
  return ll<=rr?quer(rt[r],rt[l-1],0,n,ll,rr):0;
}
int q;

int main()
{
  ///no O2
  m=rd(),q=rd();
  for(int i=1;i<=m;i++) n=max(n,a[i]=rd());
  for(int i=1;i<=m;i++) inst(i);
  while(q--)
    {
      int b=rd(),x=rd(),ll=rd(),rr=rd(),ans=0;
      for(int i=17;i>=0;i--)
        {
          int nw=ans|(((b>>i&1)^1)<<i);
          if(ok(ll,rr,nw-x,nw+(1<<i)-1-x)) ans=nw;
          else ans|=(b>>i&1)<<i;
        }
      printf("%d
",ans^b);
    }
  return 0;
}

原文地址:https://www.cnblogs.com/smyjr/p/9739276.html