zoukankan      html  css  js  c++  java
  • BZOJ4571: [Scoi2016]美味【主席树】【贪心】

    Description

    一家餐厅有 n 道菜,编号 1...n ,大家对第 i 道菜的评价值为 ai(1≤i≤n)。有 m 位顾客,第 i 位顾客的期

    望值为 bi,而他的偏好值为 xi 。因此,第 i 位顾客认为第 j 道菜的美味度为 bi XOR (aj+xi),XOR 表示异或

    运算。第 i 位顾客希望从这些菜中挑出他认为最美味的菜,即美味值最大的菜,但由于价格等因素,他只能从第

    li 道到第 ri 道中选择。请你帮助他们找出最美味的菜。

    Input

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

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

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

    (1≤n≤2×10^5,0≤ai,bi,xi<10^5,1≤li≤ri≤n(1≤i≤m);1≤m≤10^5)

    Output

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

    Sample Input

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

    Sample Output

    9
    7
    6
    7


    很僵硬。。。一开始没有想到二进制Trie和值域线段树的转化。。。长知识了

    Menci的blog写的挺好的


    思路

    首先考虑一下假设没有区间的限制怎么做?全局查找最优

    因为如果没有加的操作我们很显然是可以在二进制Trie上贪心的

    然是加上了加的操作怎么办?

    就不要强行把加和异或联系起来了

    假设我们从高到低已经选了(tmp),当前如果深度是(dep)

    我们考虑这一位如果可能是0,那么一定在([tmp,tmp+(1<<tmp)-1])范围内有数

    如果考虑(x_i),就变成了在([tmp-x_i,tmp+(1<<tmp)-1-x_i])范围内有数了

    但是直接在Trie上做区间统计非常不方便怎么办?就考虑搬到权值线段树

    实际上这两种数据结构的形态是完全相同的,只是实现方式不太一样

    那么权值线段树是不是可以实现Trie的功能呢?答案是肯定的

    所以就可以用权值线段树代替Trie了

    这样如果需要加上区间的限制,就直接上个主席树就可以了。。。

    或者你暴力可持久化Trie我没意见。。。。


    //Author: dream_maker
    #include<bits/stdc++.h>
    using namespace std;
    //----------------------------------------------
    //typename
    typedef long long ll;
    //convenient for
    #define fu(a, b, c) for (int a = b; a <= c; ++a)
    #define fd(a, b, c) for (int a = b; a >= c; --a)
    #define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a)
    //inf of different typename
    const int INF_of_int = 1e9;
    const ll INF_of_ll = 1e18;
    //fast read and write
    template <typename T>
    void Read(T &x) {
      bool w = 1;x = 0;
      char c = getchar();
      while (!isdigit(c) && c != '-') c = getchar();
      if (c == '-') w = 0, c = getchar();
      while (isdigit(c)) {
        x = (x<<1) + (x<<3) + c -'0';
        c = getchar();
      }
      if (!w) x = -x;
    }
    template <typename T>
    void Write(T x) {
      if (x < 0) {
        putchar('-');
        x = -x; 
      }
      if (x > 9) Write(x / 10);
      putchar(x % 10 + '0');
    }
    //----------------------------------------------
    const int N = 2e5 + 10;
    int rt[N], siz[N * 20], ls[N * 20], rs[N * 20], tot = 0;
    
    void pushup(int t) {
      siz[t] = siz[ls[t]] + siz[rs[t]];
    }
    
    void build(int &t, int l, int r) {
      t = ++tot;
      if (l == r) return ;
      int mid = (l + r) >> 1;
      build(ls[t], l, mid);
      build(rs[t], mid + 1, r);
    }
    
    void insert(int &t, int last, int l, int r, int vl, int dep) {
      t = ++tot;
      siz[t] = siz[last] + 1;
      ls[t] = ls[last];
      rs[t] = rs[last];
      if (l == r) return;
      int mid = (l + r) >> 1;
      if (vl & (1 << (dep - 1))) insert(rs[t], rs[last], mid + 1, r, vl, dep - 1);
      else insert(ls[t], ls[last], l, mid, vl, dep - 1);
      pushup(t);
    }
    
    bool query(int t, int last, int l, int r, int ql, int qr) {
      if (ql > qr || l > r) return 0;
      if (ql <= l && r <= qr) return siz[t] > siz[last];
      int mid = (l + r) >> 1;
      if (qr <= mid) return query(ls[t], ls[last], l, mid, ql, qr);
      else if (ql > mid) return query(rs[t], rs[last], mid + 1, r, ql, qr);
      else return query(ls[t], ls[last], l, mid, ql, mid) || query(rs[t], rs[last], mid + 1, r, mid + 1, qr);
    }
    
    int n, q, a[N], b[N], x[N], l[N], r[N];
    void init() {
      Read(n), Read(q);
      fu(i, 1, n) Read(a[i]);
      fu(i, 1, q) Read(b[i]), Read(x[i]), Read(l[i]), Read(r[i]);
    }
    int main() {
      //freopen("1.in", "r", stdin);
      init();
      int maxv = 0;
      fu(i, 1, n) maxv = max(maxv, a[i]);
      fu(i, 1, q) maxv = max(maxv, max(b[i], x[i]));
      int m = 1, dep = 0;
      while (m < maxv + 1) m <<= 1, ++dep;
      m--;
      build(rt[0], 0, m);
      fu(i, 1, n) insert(rt[i], rt[i - 1], 0, m, a[i], dep);
      fu(i, 1, q) {
        int ans = 0, tmp = 0;
        fd(j, dep, 0) {
          int now = 1 << j;
          if (b[i] & now) {
            if (query(rt[r[i]], rt[l[i] - 1], 0, m, max(0, tmp - x[i]), max(0, tmp - x[i] + now - 1))) {
              ans |= now;
            } else {
              tmp |= now;
            }
          } else {
            if (query(rt[r[i]], rt[l[i] - 1], 0, m, max(0, tmp - x[i] + now), max(0, tmp - x[i] + now * 2 - 1))) {
              ans |= now;
              tmp |= now;
            }
          }
        }
        Write(ans), putchar('
    ');
      }
      return 0;
    }
    
  • 相关阅读:
    Linux五种IO模型
    怎样理解阻塞非阻塞与同步异步的区别?
    .NET 框架 (转载)
    数组 反转
    排序 归并排序&逆序对
    快速寻找满足条件的2个数
    数组 寻找最大的第k个数
    字符串 删除字符串开始以及末尾的空白符,并把数组中间的多个空格(如果有)符转换为1个
    排序 快速排序
    java8常用api
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9866038.html
Copyright © 2011-2022 走看看