洛谷 P3293 [SCOI2016] 美味

题目描述

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

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

对于 100% 的数据,满足 1n2×1050ai,bi,xi<1051lirin1im),1m105

解析

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

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

设当前枚举到第 i 位,b 在这一位上的值为 bi.

分类讨论:

  • bi=1,要使答案最大即是让 (a+x) 的这一位为 0,容易发现当且仅当新的答案在 [ans,ans+2i1] 中,即 a[ansx,ans+2i1x].

  • bi=0,就要尽可能满足 (a+x) 的这一位为 1,当且仅当新的答案在 [ans+2i,ans+2i+11] 中, a[ans+2ix,ans+2i+11x].

只需要查找是否存在值在以上区间内的 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 @   心灵震荡  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示