洛谷 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;
}

浙公网安备 33010602011771号