[Scoi2016]美味

题意

<body> <center><h2>4571: [Scoi2016]美味</h2><span class="green">Time Limit: </span>30 Sec&nbsp;&nbsp;<span class="green">Memory Limit: </span>256 MB<br><span class="green">Submit: </span>1048&nbsp;&nbsp;<span class="green">Solved: </span>612<br>[<a href="submitpage.php?id=4571">Submit</a>][<a href="problemstatus.php?id=4571">Status</a>][<a href="bbs.php?id=4571">Discuss</a>]</center><h2>Description</h2><div class="content"><div>一家餐厅有 n 道菜,编号 1...n ,大家对第 i 道菜的评价值为 ai(1≤i≤n)。有 m 位顾客,第 i 位顾客的期</div> <div>望值为 bi,而他的偏好值为 xi 。因此,第 i 位顾客认为第 j 道菜的美味度为 bi XOR (aj+xi),XOR 表示异或</div> <div>运算。第 i 位顾客希望从这些菜中挑出他认为最美味的菜,即美味值最大的菜,但由于价格等因素,他只能从第&nbsp;</div> <div>li 道到第 ri 道中选择。请你帮助他们找出最美味的菜。</div> <div></div></div><h2>Input</h2><div class="content"><div>第1行,两个整数,n,m,表示菜品数和顾客数。</div> <div>第2行,n个整数,a1,a2,...,an,表示每道菜的评价值。</div> <div>第3至m+2行,每行4个整数,b,x,l,r,表示该位顾客的期望值,偏好值,和可以选择菜品区间。</div> <div>1≤n≤2×10^5,0≤ai,bi,xi<10^5,1≤li≤ri≤n(1≤i≤m);1≤m≤10^5</div> <div></div></div><h2>Output</h2><div class="content"><p>&nbsp;输出 m 行,每行 1 个整数,ymax ,表示该位顾客选择的最美味的菜的美味值。</p></div><h2>Sample Input</h2> <div class="content"><span class="sampledata">4 4 <br> 1 2 3 4 <br> 1 4 1 4 <br> 2 3 2 3 <br> 3 2 3 3 <br> 4 1 2 4</span></div><h2>Sample Output</h2> <div class="content"><span class="sampledata">9 <br> 7 <br> 6 <br> 7</span></div><h2>HINT</h2> <div class="content"><p></p></div><h2>Source</h2> <div class="content"><p><a href="problemset.php?search="></a></p></div><center>[<a href="submitpage.php?id=4571">Submit</a>][<a href="problemstatus.php?id=4571">Status</a>][<a href="bbs.php?id=4571">Discuss</a>]</center><br> <a href="./"><span class="red">HOME</span></a> <a href="javascript:history.go(-1)"><span class="red">Back</span></a>
</body>

分析

考试的时候一看到异或和就想到Trie,然后就想着用可持久化Trie搞一搞,但是那个加法我推不出来二进制位的变化规律。

后来才知道这种题的另一种做法,找范围。贪心地从高到低考虑\(b\)的每一位,我们尽量让他取反,假设最后选出来的\(a+x\)\(ans\),那么答案就是\(b\ XOR\ ans\)

那么假设前面的位置找出来的存在了\(ans\)里面,现在我们想让第\(i\)位取成\(to\),那么这个数值可能的范围是后面的位置全取0到后面的位置全取1。具体而言,就是找有没有以下范围的数:

\[[ans+to\cdot 2^i,ans+to\cdot 2^i +2^i-1] \]

那么用主席树就可以解决区间查询存在性问题了。由于对每个\(a\)加了\(x\),所以找的范围端点要减去\(x\)

时间复杂度\(O(n \log n +m \log^2n)\)

代码

主席树要开到\(N*18\),也就是\(N*(\lceil \log_2 N \rceil+1)\),尽管\(b<10^5\),只有17位,但是\(a+x < 2 \times 10^5\),有18位,所以\(b\)要考虑18位,而不是17位。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;
    rg char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x){
    return x=read<T>();
}
typedef long long ll;
using namespace std;

co int N=2e5+1;
int root[N],tot,s[N*18],L[N*18],R[N*18];
void insert(int&x,int l,int r,int p){
	++tot,s[tot]=s[x]+1,L[tot]=L[x],R[tot]=R[x],x=tot;
	if(l==r) return;
	int mid=l+r>>1;
	if(p<=mid) insert(L[x],l,mid,p);
	else insert(R[x],mid+1,r,p);
}
int query(int x,int y,int l,int r,int ql,int qr){
	if(qr<l||ql>r||!x&&!y) return 0;
	if(ql<=l&&r<=qr) return s[y]-s[x];
	int mid=l+r>>1;
	if(qr<=mid) return query(L[x],L[y],l,mid,ql,qr);
	if(ql>mid) return query(R[x],R[y],mid+1,r,ql,qr);
	return query(L[x],L[y],l,mid,ql,qr)+query(R[x],R[y],mid+1,r,ql,qr);
}
int main(){
//	freopen("food.in","r",stdin),freopen("food.out","w",stdout);
	int n=read<int>(),m=read<int>();
	for(int i=1;i<=n;++i) insert(root[i]=root[i-1],0,99999,read<int>());
	for(int b,x,l,r,ans;m--;){
		read(b),read(x),read(l),read(r),ans=0;
		for(int i=17,to;i>=0;--i){
			to=~b>>i&1;
			ans+=(query(root[l-1],root[r],0,99999,ans+(to<<i)-x,ans+(to<<i)+(1<<i)-1-x)?to:!to)<<i;
		}
		printf("%d\n",b^ans);
	}
	return 0;
}

posted on 2019-03-08 13:36  autoint  阅读(150)  评论(0编辑  收藏  举报

导航