洛谷 P3293 [SCOI2016] 美味

题目描述

一家餐厅有 \(n\) 道菜,编号 \(1, 2, \ldots, n\),大家对第 \(i\) 道菜的评价值为 \(a_i\)。有 \(m\) 位顾客,第 \(i\) 位顾客的期望值为 \(b_i\),而他的偏好值为 \(x_i\)。因此,第 \(i\) 位顾客认为第 \(j\) 道菜的美味度为 \(b_i\oplus (a_j + x_i)\)\(\oplus\) 表示异或运算。

\(i\) 位顾客希望从这些菜中挑出他认为最美味的菜,即美味值最大的菜,但由于价格等因素,他只能从第 \(l_i\) 道到第 \(r_i\) 道中选择。请你帮助他们找出最美味的菜。

对于 \(100\%\) 的数据,满足 \(1 \le n \le 2 \times 10^5\)\(0 \le a_i,b_i,x_i < 10^5\)\(1 \le l_i \le r_i \le n\)\(1 \le i \le m\)),\(1 \le m \le 10^5\)

解析

看到异或就考虑按位贪心一下。

从高位按二进制开始枚举,记录一个满足题目限制的 \(ans = a + x\).

设当前枚举到第 \(i\) 位,\(b\) 在这一位上的值为 \(b'_i\).

分类讨论:

  • \(b'_i = 1\),要使答案最大即是让 \((a+x)\) 的这一位为 \(0\),容易发现当且仅当新的答案在 \([ans, ans + 2^i - 1]\) 中,即 \(a \in [ans - x, ans + 2^i - 1 - x]\).

  • \(b'_i = 0\),就要尽可能满足 \((a+x)\) 的这一位为 \(1\),当且仅当新的答案在 \([ans + 2^i, ans + 2^{i + 1} - 1]\) 中, \(a \in [ans + 2^i - x, ans + 2^{i + 1} - 1 - x]\).

只需要查找是否存在值在以上区间内的 \(a\) 即可。如果存在就取最优的答案,否则这一位只能取另外的值。

在某个区间 \([l, r]\) 内查找值域在某个区间 \([L, R]\) 中的数,就是经典的可持久化线段树板子了(虽然也能用树套树做)。

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 5;

int n, m, a[N], b, x, l, r, idx, lc[N << 5], rc[N << 5], val[N << 5], root[N];

void pushup(int x)
{
	val[x] = val[lc[x]] + val[rc[x]];
}

int build(int x, int l, int r)
{
	if(!x) x = ++idx;
	if(l == r) return x;
	int mid = (l + r) >> 1;
	lc[x] = build(lc[x], l, mid);
	rc[x] = build(rc[x], mid + 1, r);
	return x; 
}

int update(int x, int y, int p, int l, int r)
{
	if(y == x || !y)
	{
		y = ++idx;
		lc[y] = lc[x];
		rc[y] = rc[x];
		val[y] = val[x];
	}
	if(l == r && l == p)
	{
		val[y]++;
		return y;
	}
	int mid = (l + r) >> 1;
	if(p <= mid) lc[y] = update(lc[x], lc[y], p, l, mid); 
	if(mid < p) rc[y] = update(rc[x], rc[y], p, mid + 1, r);
	pushup(y);
	return y;
}

int query(int x, int y, int L, int R, int l, int r) // 查询版本 [x+1, y] 中的区间 [L, R] 
{
	if(l > r) return 0;
	if(L <= l && r <= R) return val[y] - val[x];
	int mid = (l + r) >> 1, res = 0;
	if(L <= mid) res += query(lc[x], lc[y], L, R, l, mid);
	if(mid < R) res += query(rc[x], rc[y], L, R, mid + 1, r);
	return res;
}

int main()
{
	ios :: sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cin >> n >> m;
	root[0] = build(root[0], 0, 1e5);
	for(int i = 1; i <= n; i++)
		cin >> a[i], root[i] = update(root[i - 1], root[i], a[i], 0, 1e5);
	for(int i = 1; i <= m; i++)
	{
		cin >> b >> x >> l >> r;
		int ans = 0;
		for(int j = 20; j >= 0; j--)
		{
			int o = b & (1 << j);
			if(!o) ans += (1 << j) * (bool) query(root[l - 1], root[r], ans + (1 << j) - x, ans + (1 << (j + 1)) - x - 1, 0, 1e5);
			else ans += (1 << j) * !(bool) query(root[l - 1], root[r], ans - x, ans + (1 << j) - x - 1, 0, 1e5); 
		}
		cout << (ans ^ b) << '\n';
	}	
	return 0;
}
posted @ 2024-12-26 15:37  心灵震荡  阅读(33)  评论(0)    收藏  举报