CF2161G Bitwise And Equals

考虑怎么写暴力。首先把所有 \(a\) 都调整成 \(X\) 的超集。设调整后的数组 \(a'_i=b_i+X\),即 \(b_i\)\(a'_i\) 扣掉 \(X\) 的部分。

此时,令 \(Y\)\(b_i\)\(\mathrm{AND}\) 和,那么我们还需要把 \(Y\) 中的位变成 \(0\)

一个显然的观察是,对于某一个元素操作,只会把 \(b_i\) 的一段前缀 \(0\sim w-1\) 位置成 \(0\),然后把 \(w\) 为变成 \(1\)。因为我们消除的是一段前缀,所以也一定只会操作一个数。同时一个限制是这样调整完的 \(w\) 位不能全变成 \(1\),即原来 \(w\)\(=1\) 个数 \(\le n-2\)

那么可以枚举 \(w\),此处需要保证 \(w>\mathrm{highbit}(Y)\),如果 \(=1\) 个数 \(\le n-2\),就取出第 \(w\)\(=0\) 的,只考虑 \(0\sim w-1\) 位的最大 \(a_i\)。然后算一下贡献即可。

考虑怎么优化这一过程。\(Y\) 的最高位可以直接算。对于一个 \(a_i\),它调整成 \(X\) 超集,一定形如找出最高位 \(d\),使 \(X\)\(d\)\(=1\)\(a_i\)\(d\)\(=0\),然后 \(a_i\)\(>d\) 位不变,\(\le d\) 位变成和 \(X\) 相同。称这样的 \(d\) 为其切割位置。

枚举切割位 \(w_2\),那么合法的 \(a_i\) 需要满足如下条件:

  • \(a_i\) 的第 \(w_2\)\(=0\),且对于 \(t>w_2\),如果 \(X\)\(t\)\(=1\),则 \(a_i\) 这一位也 \(=1\)

\(a_i\) 对于答案的贡献也是好算的。如果 \(w_2\ge w\),那么说明此时所有合法的 \(a_i\)\(0\sim w\) 位都被改成和 \(X\) 相同。那么只要判一下是否存在即可。如果 \(w_2<w\),那么我们只要找到考虑 \(0\sim w\) 位时最大的 \(a_i\) 即可。这些都可以直接枚举 \(w, w_2\),高维后缀和预处理出来,总复杂度是 \(O(V\log^2V)\) 的。

对于计算 \(a_i\) 变成 \(X\) 超集的部分,也可以同理枚举 \(w_2\),算法是类似的。

总复杂度 \(O(V\log^2 V)\)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

void Chkmax(int &x, int y) {
  x = max(x, y);
}

const int inf = 1e9;
const int kN = 2e5 + 5, kLog = 22, kV = (1 << 20) + 5;
int n, q, andsum = (1 << 20) - 1;
int a[kN], b[kN];
int *f[kLog][kLog], *c[kLog][kLog];
ll *sum[kLog];
int *num[kLog];

template <typename T>
void FWT_Sum(int n, T *f) {
  for(int i = 0; i < n; i++) {
    for(int j = 0; j < (1 << n); j += (2 << i)) {
      int p = j, q = j + (1 << i);
      for(; p < j + (1 << i); p++, q++) {
        f[p] += f[q];
      }
    }
  }
}

ll Query(int x) {
  ll res = 0;
  for(int w2 = -1; w2 < 20; w2++) {
    if(!((x >> w2) & 1)) continue;
    res -= sum[w2 + 1][x >> w2 + 1];
    res += (ll)num[w2 + 1][x >> w2 + 1] * (x & ((1 << w2 + 1) - 1));
  }
  int asum = andsum;
  asum ^= (asum & x);
  if(!asum) return res;
  int wy = __lg(asum);
  int tmp = (~andsum) & x;
  if(tmp && __lg(tmp) > wy) return res;
  int mn = inf;
  for(int w1 = wy; w1 < 21; w1++) {
    if((x >> w1) & 1) continue;
    int t = -inf, cnt = 0;
    for(int w2 = -1; w2 < 20; w2++) {
      if(~w2 && !((x >> w2) & 1)) continue;
      int mx = -inf;
      if(w1 <= w2) {
        if(c[w1][w2 + 1][x >> w2 + 1]) {
          t = max(t, 0);
        }
      }else {
        int mx = f[w1][w2 + 1][x >> w2 + 1];
        int ct = c[w1][w2 + 1][x >> w2 + 1];
        cnt += ct;
        if(mx >= 0) {
          mx &= ~((1 << w2 + 1) - 1);
          t = max(t, mx ^ (mx & x));
        }
      }
    }
    if(cnt <= n - 2) {
      mn = min(mn, (1 << w1) - t);
    }
  }
  return res + mn;
}

int main() {
  // freopen("1.in", "r", stdin);
  // freopen("1.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> q;
  for(int i = 1; i <= n; i++) {
    cin >> a[i];
    andsum &= a[i];
  }
  for(int w2 = -1; w2 < 20; w2++) {
    int len = (1 << 19 - w2);
    sum[w2 + 1] = new ll [len + 5];
    memset(sum[w2 + 1], 0, (len + 5) * sizeof(ll));
    num[w2 + 1] = new int [len + 5];
    memset(num[w2 + 1], 0, (len + 5) * sizeof(int));
  }
  for(int w1 = 0; w1 < 21; w1++) {
    for(int w2 = -1; w2 < 20; w2++) {
      if(w1 == w2) continue;
      int len = (1 << 19 - w2);
      if(w1 > w2) {
        f[w1][w2 + 1] = new int [len + 5];
        memset(f[w1][w2 + 1], 0xcf, (len + 5) * sizeof(int));
      }
      c[w1][w2 + 1] = new int [len + 5];
      memset(c[w1][w2 + 1], 0, (len + 5) * sizeof(int));
    }
  }
  for(int i = 1; i <= n; i++) {
    for(int w2 = -1; w2 < 20; w2++) {
      if(~w2 && (a[i] >> w2) & 1) continue;
      int p = a[i] >> w2 + 1;
      sum[w2 + 1][p] += (a[i] & ((1 << w2 + 1) - 1));
      num[w2 + 1][p]++;
      for(int w1 = 0; w1 < 21; w1++) {
        if(w1 == w2) continue;
        int v = a[i] & ((1 << w1) - 1);
        if(w1 <= w2) c[w1][w2 + 1][p]++;
        else if(!((a[i] >> w1) & 1)) {
          Chkmax(f[w1][w2 + 1][p], v);
        }else c[w1][w2 + 1][p]++;
      }
    }
  }
  for(int w2 = -1; w2 < 20; w2++) {
    FWT_Sum(19 - w2, sum[w2 + 1]);
    FWT_Sum(19 - w2, num[w2 + 1]);
  }
  for(int w1 = 0; w1 < 21; w1++) {
    for(int w2 = -1; w2 < 20; w2++) {
      if(w1 == w2) continue;
      int wl = 19 - w2;
      int *ff = f[w1][w2 + 1];
      FWT_Sum(wl, c[w1][w2 + 1]);
      if(w1 > w2) {
        for(int i = 0; i < wl; i++) {
          for(int j = 0; j < (1 << wl); j += (2 << i)) {
            int p = j, q = j + (1 << i);
            for(; p < j + (1 << i); p++, q++) {
              ff[p] = max(ff[p], ff[q]);
            }
          }
        }
      }
    }
  }
  while(q--) {
    int x;
    cin >> x;
    cout << Query(x) << "\n";
  }
  return 0;
}
posted @ 2025-12-05 09:01  CJzdc  阅读(4)  评论(0)    收藏  举报