20200916 SCOI模拟T2(按位贪心+主席树)

T2 P3293 [SCOI2016]美味

思路:
按照数位一位一位的贪心,加了一个 x x x,考虑对于所有的 a i + x a_i+x ai+x b b b 的按位异或
假设我们已经处理到 b b b 的第 i i i 位,假设是 1 1 1
那么我们只需要查找是否存在 a j + x aj+x aj+x 使得其二进制第 i i i 位数字是 0 0 0,设当前结果是 a n s ans ans,那么我们需要查找的数的大小就是在区间 [ a n s − x , a n s + ( 1 < < i ) − 1 − x ] [ans-x,ans+(1<<i)-1-x] [ansx,ans+(1<<i)1x],显然这个区间前 i − 1 i-1 i1 位都相同
两个区间限制,主席树维护

代码:

#include <bits/stdc++.h>
using namespace std;
namespace IO {
char _buf[1 << 21], *_p1 = _buf, *_p2 = _buf;
#define ch()                                                                 \
  (_p1 == _p2 &&                                                             \
           (_p2 = (_p1 = _buf) + fread(_buf, 1, 1 << 21, stdin), _p1 == _p2) \
       ? EOF                                                                 \
       : *_p1++)
inline int in() {
  int s = 0, f = 1;
  char x = ch();
  for (; x < '0' || x > '9'; x = ch())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char buf_[1 << 21];
int p1_ = -1;
inline void flush() {
  fwrite(buf_, 1, p1_ + 1, stdout);
  p1_ = -1;
}
inline void pc(char x) {
  if (p1_ == (1 << 21) - 1) flush();
  buf_[++p1_] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) {
    pc('0');
    return;
  }
  if (x < 0) {
    pc('-');
    x = -x;
  }
  while (x) {
    k[++pos] = (x % 10) | 48;
    x /= 10;
  }
  for (int i = pos; i; i--) pc(k[i]);
  return;
}
inline void out(string x) {
  int k = x.size();
  for (int i = 0; i < k; i++) pc(x[i]);
}
}  // namespace IO
using namespace IO;

const int A = 2e5 + 5;
const int Max = 1e5;
const int logA = 20;
int n, m;
int val[A];
struct SGT {
  int ls, rs;
  int num;
} tr[4 * logA * A];
int rt[A], tot;

#define ls(x) tr[x].ls
#define rs(x) tr[x].rs
inline void build(int y, int &x, int l, int r, int val) {
  x = ++tot;
  tr[x] = tr[y];
  tr[x].num++;
  if (l == r) return;
  int mid = (l + r) >> 1;
  if (val <= mid)
    build(ls(y), ls(x), l, mid, val);
  else
    build(rs(y), rs(x), mid + 1, r, val);
  return;
}
inline int find(int x, int y, int ql, int qr, int l, int r) {
  if (tr[x].num == tr[y].num || qr < l || ql > r) return 0;
  if (l >= ql && r <= qr) return tr[x].num - tr[y].num;
  int mid = (l + r) >> 1;
  int num = 0;
  if (ql <= mid) num += find(ls(x), ls(y), ql, qr, l, mid);
  if (qr >= mid + 1) num += find(rs(x), rs(y), ql, qr, mid + 1, r);
  return num;
}
#undef ls
#undef rs

signed main() {
  n = in(), m = in();
  for (int i = 1; i <= n; i++) val[i] = in();
  for (int i = 1; i <= n; i++) build(rt[i - 1], rt[i], 0, Max, val[i]);
  while (m--) {
    int b = in(), x = in(), l = in(), r = in();
    int ans = 0;
    for (int i = logA; ~i; i--) {
      if (b & (1 << i) &&
          !find(rt[r], rt[l - 1], ans - x, ans - x + (1 << i) - 1, 0, Max))
        ans |= (1 << i);
      if (!(b & (1 << i)) && find(rt[r], rt[l - 1], ans - x + (1 << i),
                                  ans - x + (1 << (i + 1)) - 1, 0, Max))
        ans |= (1 << i);
    }
    out(ans ^ b), pc('\n');
  }
  flush();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值