BZOJ 4571 【SCOI2016】 美味
题目链接:美味
如果题目里面没有那个\(a_i\),这道题就可以直接在\(Trie\)树上走一走就做完了。现在多了个\(a_i\),\(Trie\)树就无能为力了。
我们考虑一下在\(Trie\)树上走的过程。我们从高位往低位按位贪心,每次判定这一位能否取\(1\),本质上就是在判定某个区间内有没有数。所以我们把这个区间弄出来,在主席树上查询一下就可以了。这样就可以把那个\(a_i\)给考虑进来了。
下面贴代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) #define maxn 200010 #define MAXN 5000010 using namespace std; typedef long long llg; int n,m,rt[maxn],a[maxn],_m,tt; int sumv[MAXN],le[MAXN],ri[MAXN],L,R,_sum; int getint(){ int w=0;bool q=0; char c=getchar(); while((c>'9'||c<'0')&&c!='-') c=getchar(); if(c=='-') c=getchar(),q=1; while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } int build(int u,int l,int r){ int mid=(l+r)>>1,v=++tt; le[v]=le[u],ri[v]=ri[u],sumv[v]=sumv[u]+1; if(l!=r){ if(L<=mid) le[v]=build(le[u],l,mid); else ri[v]=build(ri[u],mid+1,r); } return v; } void query(int u,int v,int l,int r){ int mid=(l+r)>>1; if(l>=L && R>=r){_sum+=sumv[v]-sumv[u];return;} if(L<=mid) query(le[u],le[v],l,mid); if(R>mid) query(ri[u],ri[v],mid+1,r); } bool ask(int u,int v,int l,int r){ L=max(l,0); R=min(r,_m); if(L>R) return 0; _sum=0; query(rt[u-1],rt[v],0,_m); return _sum!=0; } int main(){ File("a"); n=getint(),m=getint(); for(int i=1;i<=n;i++) _m=max(_m,a[i]=getint()); for(int i=1;i<=n;i++) L=a[i],rt[i]=build(rt[i-1],0,_m); while(m--){ int b=getint(),x=getint(); int l=getint(),r=getint(),ans=0; for(int i=17;i>=0;i--){ int now=ans+((1^(b>>i&1))<<i); if(ask(l,r,now-x,now+(1<<i)-1-x)) ans=now; else ans+=(b>>i&1)<<i; } printf("%d\n",ans^b); } return 0; }