gym102331B Bitwise Xor

gym102331B Bitwise Xor

和我找到的题解都不同的做法。感觉简单一些。

首先将 \(a\) 排序,从高位往低位考虑,假设考虑第 \(p\) 位,不难发现这时序列按照第 \(p\) 位取值被划分为两部分,我们注意到如果 \(x\) 的这一位是 \(0\) 那么这两部分各取两个数异或起来一定满足限制,故两部分互互不影响,可以分别递归下去把方案数乘起来就是总方案数。而如果 \(x\) 的这一位是 \(1\) 那么我们根据鸽巢原理至多只能取两个数,这时候就相当于取两个数异或大于等于 \(x\),可以直接用一个 Trie 做。然后就做完了。注意复杂度不是 \(O(n \log a)\) 而是 \(O(n \log (na))\)

const int N = 3e5 + 10;
int n;
LL x, a[N];

struct Trie {
  int ch[N * 60][2], tr[N * 60];
  int tot = 1;
  int newNode() {
    ++tot;
    tr[tot] = 0;
    ch[tot][0] = ch[tot][1] = 0;
    return tot;
  }
  void clear() {
    tot = 0; newNode();
  }
  void insert(LL x) {
    int u = 1;
    for(int i = 59; ~i; --i) {
      ++tr[u];
      int t = x >> i & 1;
      if(!ch[u][t]) ch[u][t] = newNode();
      u = ch[u][t];
    }
    ++tr[u];
  }
  int query(LL x, LL y) {
    int u = 1, res = 0;
    for(int i = 59; ~i && u; --i) {
      int t = y >> i & 1, p = x >> i & 1;
      if(t == 0) {
        res += tr[ch[u][p ^ 1]];
        u = ch[u][p];
      } else u = ch[u][p ^ 1];
    }
    return res + tr[u];
  }
}trie;

const int P = 998244353;
void add(int &x, int y) {
  (x += y) >= P ? x -= P : 0;
}
void sub(int &x, int y) {
  (x -= y) < 0 ? x += P : 0;
}

int solve(int l, int r, int p) {
  if(l > r) return 0;
  if(x >> p & 1) {
    int ans = r - l + 1;
    trie.clear();
    for(int i = l; i <= r; ++i) {
      a[i] &= (1ll << (p + 1)) - 1;
      add(ans, trie.query(a[i], x)), trie.insert(a[i]);
    }
    return ans;
  }
  int mid;
  for(mid = l; mid <= r; ++mid)
    if(a[mid] >> p & 1) break;
  return (1ll * (solve(l, mid - 1, p - 1) + 1) * (solve(mid, r, p - 1) + 1) % P - 1 + P) % P;
}

int main() {
  read(n, x);
  if(x == 0) {
    int ans = 1;
    for(int i = 1; i <= n; ++i)
      add(ans, ans);
    printf("%d\n",ans - 1);
    return 0;
  }
  for(int i = 1; i <= n; ++i)
    read(a[i]);
  sort(a + 1, a + n + 1);
  printf("%d\n",solve(1, n, 59));
}

posted @ 2023-10-04 22:35  DCH233  阅读(19)  评论(0编辑  收藏  举报