[洛谷P3293] [SCOI2016]美味

洛谷题目链接:[SCOI2016]美味

题目描述

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

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

输入输出格式

输入格式:

第1行,两个整数,n,m,表示菜品数和顾客数。

第2行,n个整数,a1,a2,...,an,表示每道菜的评价值。

第3至m+2行,每行4个整数,b,x,l,r,表示该位顾客的期望值,偏好值,和可以选择菜品区间。

输出格式:

输出 m 行,每行 1 个整数,ymax ,表示该位顾客选择的最美味的菜的美味值。

输入输出样例

输入样例#1:

4 4
1 2 3 4
1 4 1 4
2 3 2 3
3 2 3 3
4 1 2 4

输出样例#1:

9
7
6
7

说明

对于所有测试数据,\(1<=n<=2*10^ 5,0<=ai,bi,xi<10 ^ 5,1<=li<=ri<=n(1<=i<=m),1<=m<=10^5\)

题解: 如果没有这个\(+x\)的操作,就可以直接用\(trie\)查询最大异或值了,那么有这个\(+x\)的操作要怎么办呢?

我们还是一样的建出\(trie\)树,然后以二进制枚举每一位的值.

假设现在正在枚举第\(i\)位的答案\((i\in \{0, 1, 2, ...\})\),填满第\(i\)位之前的结果为\(ans\),那么以贪心的思想,这一位填入\(((1\ xor\ {((b>>i)\&1))}<<i)\)一定是最优的,然后填完这一位之后后面还有\(i\)个位置可以填入\(0\)\(1\),那么这些数字任意填入后的取值范围就是\([0,(1<<i)-1]\),如果在\(trie\)树中可以找到存在值在\([ans+((1\ xor\ {((b>>i)\&1))}<<i)-x,ans+((1\ xor\ {((b>>i)\&1))}<<i)-(1<<i)-1]\)范围内的,说明这一位可以贪心的选,否则只能在这一位填入\((({((b>>i)\&1))}<<i)\).

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;

int n, m, a[N], cnt = 0, root[N], ans = 0, mx;

struct trie{ int ch[2], size; }t[N*20];

void update(int &x, int last, int pos, int l = 0, int r = mx){
    x = ++cnt; t[x] = t[last], t[x].size++;
    if(l == r) return; int mid = (l+r>>1);
    if(pos <= mid) update(t[x].ch[0], t[last].ch[0], pos, l, mid);
    else update(t[x].ch[1], t[last].ch[1], pos, mid+1, r);
}

int query(int x, int last, int ql, int qr, int l = 0, int r = mx){
    if(ql <= l && r <= qr) return t[x].size-t[last].size;
    int mid = (l+r>>1), sum = 0;
    if(t[x].ch[0] && ql <= mid) sum += query(t[x].ch[0], t[last].ch[0], ql, qr, l, mid);
    if(t[x].ch[1] && mid < qr) sum += query(t[x].ch[1], t[last].ch[1], ql, qr, mid+1, r);
    return sum;
}

int check(int x, int y, int l, int r){
    l = max(0, l), r = min(r, mx); if(l > r) return 0;
    return query(root[y], root[x], l, r);
}

int main(){
    int b, x, l, r; cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> a[i], mx = max(mx, a[i]);
    for(int i = 1; i <= n; i++) update(root[i], root[i-1], a[i]);
    for(int i = 1; i <= m; i++){
        cin >> b >> x >> l >> r, ans = 0;
        for(int j = 19; j >= 0; j--){
            int tmp = ans+((((b >> j) & 1) ^ 1) << j);
            if(check(l-1, r, tmp-x, tmp+(1 << j)-1-x)) ans = tmp;
            else ans += (((b >> j) & 1) << j);
        }
        cout << (ans^b) << endl;
    }
    return 0;
}
posted @ 2019-03-03 20:21  Brave_Cattle  阅读(130)  评论(0编辑  收藏  举报