【题解】[SCOI2016]美味

[SCOI2016]美味

( ext{Solution:})

第一种感觉是可持久化 Trie 的样子。但是如果这样做, (x) 的限制是没有办法消去的。

这里陷入了某种错误的思考状态,发现 Trie 上分类讨论无果,从题解中学到了另一种做法:

Trie 树的本质是每一个节点下的子树都代表了一个值域。我们联想到,主席树同样可以是值域线段树,它也可以代表一个值域。

那能不能用更普遍的主席树去代替 Trie 呢?

考虑对于 (b) 的某二进制位的取值, (a[i]) 应该取什么。

那么,若 (b) 的第 (i) 位为 (1) 那么 (a[i]) 这一位是 (0) 更优秀。

那么,令当前找到的最优结果是 (A,) (ain{[A-x,A+(1<<i)-1-x]}) 这样使得 (a+x) 的对应二进制位一定非 (1).

其他情况类似。

那么我们就得到了需要的 (a) 的值域! 而前文我们恰好对 (a) 以值域建立了主席树。

于是,在主席树上查询即可。复杂度 (O(mlog^2 v).)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+10;
const int M=(1<<25);
struct Tr{int ls,rs,sum;}tr[MAXN<<5];
int node,rt[MAXN],n,m,a[MAXN];
inline int Min(int x,int y){return x<y?x:y;}
inline int Max(int x,int y){return x>y?x:y;}
inline int read() {
	int s=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)) {
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(isdigit(ch)) {
		s=s*10-48+ch;
		ch=getchar();
	}
	return w*s;
}
int change(int x,int l,int r,int pos,int v){
	int p=++node;
	tr[p]=tr[x];
	tr[p].sum+=v;
	if(l==r)return p;
	int mid=(l+r)>>1;
	if(pos<=mid)tr[p].ls=change(tr[p].ls,l,mid,pos,v);
	else tr[p].rs=change(tr[p].rs,mid+1,r,pos,v);
	return p;
}
int query(int x,int y,int L,int R,int l,int r){
	if(L>=l&&R<=r)return tr[y].sum-tr[x].sum;
	int mid=(L+R)>>1,res=0;
	if(l<=mid)res=query(tr[x].ls,tr[y].ls,L,mid,l,r);
	if(mid<r)res+=query(tr[x].rs,tr[y].rs,mid+1,R,l,r);
	return res;
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;++i)a[i]=read();
	for(int i=1;i<=n;++i)rt[i]=change(rt[i-1],0,M,a[i],1);
	for(int i=1;i<=m;++i){
		int b=read(),x=read(),l=read(),r=read();
		int ans=0;
		for(int j=23;j>=0;--j){
			int L=0,R=M,flag;
			if((b>>j)&1)L=ans,R=ans+(1<<j)-1,flag=0;
			else L=ans+(1<<j),R=ans+(1<<(j+1))-1,flag=1;
			L-=x;R-=x;
			L=Max(L,0);R=Min(R,M);
			if(!query(rt[l-1],rt[r],0,M,L,R))flag^=1;//the direction of node
			ans+=(flag<<j);
		}
		printf("%d
",ans^b);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/h-lka/p/14965957.html