「SCOI2016」美味

「SCOI2016」美味

题目描述

一家餐厅有 \(n\) 道菜,编号 \(1 \ldots n\) ,大家对第 \(i\) 道菜的评价值为 \(a_i \:( 1 \leq i \leq n )\)。有 \(m\) 位顾客,第 \(i\) 位顾客的期望值为 \(b_i\),而他的偏好值为 \(x_i\)。因此,第 \(i\) 位顾客认为第 \(j\) 道菜的美味度为 \(b_i\ \text{xor} \ (a_j+x_i)\) (\(\text{xor}\) 表示异或运算)。 第 \(i\) 位顾客希望从这些菜中挑出他认为最美味的菜,即美味值最大的菜,但由于价格等因素,他只能从第 \(l_i\) 道到第 \(r_i\) 道中选择。请你帮助他们找出最美味的菜。

解题思路 :

问题要求从区间里找一个数 \(a_j\) ,使得 \((a_j + x_i) \text{ xor } b_i\) 最大,先考虑当 \(x_i = 0\) 的时候是一个经典问题该怎么做

\(x_i = 0\) 时显然直接用一个可持久化 \(Trie\) 树按位维护区间内所有 \(a_j\) 查询即可

但是有加上 \(x_i\) 的操作由于 \(Trie\) 的局限性就不那么可做了,不妨考虑上述算法的本质

\(Trie\) 树从高到低走的每一位本质上是对最终选的数的可行区间不断减少

设当前第 \(i\) 位的大小是 \(2^i\) ,那么如果选了它答案一定 $ \geq 2^i$ ,否则答案一定 \(< 2 ^i\) ,因为有 \(\sum_{j=0}^{i-1} 2^j =2^i - 1\)

所以我们可以用一个线段树维护 \([0, 2^{lim-1}]\) 的权值来代替原本的 \(Trie\),每次走左儿子本质上是这一位选了 \(0\),走右儿子本质上是选了 \(1\)

考虑查询加上 \(x_i\) 后的 \(a_j\) 本质上是将原来的 \(a_j - x_i\) 代替新的 \(a_j\) ,只需要查询的区间更改一下就可以了

因为要回答区间的答案,所以还要把线段树可持久化一下,每次查询从高到低枚举 \(b_i\) 的每一位看看能否选相反的即可




/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int f = 0, ch = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}

const int N = 1000005, len = (1 << 19) - 1;
int rt[N], n, m;

struct SegmentTree{
	int sz[N*25], lc[N*25], rc[N*25], size;
	inline void ins(int &u, int pr, int l, int r, int pos){
		u = ++size;
		lc[u] = lc[pr], rc[u] = rc[pr], sz[u] = sz[pr] + 1;
		if(l == r) return;
		int mid = l + r >> 1;
		if(pos <= mid) ins(lc[u], lc[pr], l, mid, pos);
		else ins(rc[u], rc[pr], mid + 1, r, pos);
	}
	inline int query(int x, int y, int l, int r, int L, int R){
		if(l >= L && r <= R) return (sz[y] - sz[x] > 0);
		int mid = l + r >> 1, res = 0;
		if(L <= mid) res |= query(lc[x], lc[y], l, mid, L, R);
		if(mid < R) res |= query(rc[x], rc[y], mid + 1, r, L, R);
		return res;
	}
}van;

int main(){
	read(n), read(m);
	for(int i = 1, x; i <= n; i++) 
		read(x), van.ins(rt[i], rt[i-1], 0, len, x);
		
	while(m--){
		int now, add, l = 0, r = len, res = 0, L, R;
		read(now), read(add), read(L), read(R);
		for(int i = 18; ~i; i--){
			int s = ((now >> i) & 1) ^ 1, mid = l + r >> 1;
			int nl = s ? mid + 1 : l, nr = s ? r : mid;
			if(van.query(rt[L-1], rt[R], 0, len, Max(nl - add, 0), Max(nr - add, 0)))
				res |= 1 << i, l = nl, r = nr;
			else l = s ? l : mid + 1, r = s ? mid : r;
		}
		printf("%d\n", res);
	}		
	return 0;		
}

posted @ 2018-08-16 22:10  Joyemang33  阅读(377)  评论(0编辑  收藏  举报