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;
}
posted @ 2017-03-28 15:33  lcf2000  阅读(261)  评论(1编辑  收藏  举报