CF2056F2 Xor of Median (Hard Version)

如果我们知道了每个数的出现次数 \(cnt\),那么用这些数重排成序列的方案数是组合数连乘。根据 Lucas 定理的结论,重排方案数为奇数当且仅当 \(cnt\) 加和不进位。也就是说,每个不为 \(0\)\(cnt\)\(n\) 的所有为 \(1\) 位划分为若干个集合。

又因为题目限制 \(cnt\) 不为 \(0\) 的位置满足 \(cnt\) 不减,于是可以认为我们把 \(n\) 的所有为 \(1\) 位划分为若干无标号集合,然后选出 \(cnt\) 不为 \(0\) 的值后按照最高位从小往大依次分配。

而且可以发现,此时序列的中位数也就是分配到 \(n\) 的最高位的值。不妨设为 \(x\)

考虑枚举 \(x\) 和划分的集合数 \(c\),那么 \(x\) 会贡献 \(\dbinom{x}{c-1}\times\begin{Bmatrix}{tot}\\{c}\end{Bmatrix}\) 次,其中 \(tot\)\(n\) 中为 \(1\) 的二进制位数。暴力实现可以通过 F1。

不妨改为枚举 \(c\)\(\begin{Bmatrix}{tot}\\{c}\end{Bmatrix}\) 是固定的,稍后再处理。我们现在只要求 \(\dbinom{x}{c-1}\) 为奇数,且 \(0\le x<m\)\(x\) 的异或和。根据 Lucas 定理,这个组合数为奇数相当于 \(x\)\(c-1\) 的超集。

于是可以从高往低,搜索 \(x\) 每一位是否为 \(1\),如果 \(x\) 此时已经 \(\ge m\) 就退出;如果 \(x\) 未枚举的位可以任意填,就直接算异或和。这样的时间复杂度为 \(O(\log m)\),可以接受。

现在我们只要考虑 \(\begin{Bmatrix}{tot}\\{c}\end{Bmatrix} \bmod 2\) 怎么求了。一个简单的做法是 bitset,但是这个做法数据范围再大一点就跑不动了。我们考虑一个更高效的做法。

直接考虑 \(\begin{Bmatrix}{n}\\{m}\end{Bmatrix}\bmod 2\) 的求法。其组合意义就是 \(n\) 个不同元素划分到 \(m\) 个无序集合。可以钦定每个集合的代表元,不妨令一个集合的代表元就是其中编号最小元素。那么一个未钦定的元素,其分配方案数就是之前的代表元个数。总方案数就是把这个值对每个未钦定元素求乘积。

我们希望这个乘积是奇数,也就是说,不存在未钦定的元素满足之前有偶数个代表元。这个条件相当于第 \(2t\) 个代表元必须和第 \(2t+1\) 个相邻。这里方便起见,我们假设 \(m\) 为奇数,偶数的时候最后一个代表元必须在 \(n\),就转化为奇数的情况了。

显然也有第一个代表元一定在 \(1\),那么就是在剩下的 \(n-1\) 个位置中放 \(m-1\) 个元素,要求 \(2t\)\(2t+1\) 必须相邻。这个就可以直接组合数算了,方案数就是 \(\dbinom{n-1-\frac{m-1}{2}}{\frac{m-1}{2}}\)

\(m\) 为偶数也是类似的,可以得到一个较为简洁的形式:\(\dbinom{n-1-\lfloor\frac{m}{2}\rfloor}{\lfloor\frac{m-1}{2}\rfloor}\)。那么这个判一下子集即可。

于是我们可以用 \(O(1)\) 的复杂度求 \(\begin{Bmatrix}{tot}\\{c}\end{Bmatrix} \bmod 2\),整个题复杂度 \(O(k\log m)\)

#include <bits/stdc++.h>
#define ALL(x) begin(x), end(x)
using namespace std;
void file() {
  freopen("1.in", "r", stdin);
  freopen("1.out", "w", stdout);
}
using ll = long long;

int k, m;
string str;

bool S(int n, int m) {
  int x = n - 1 - m / 2, y = (m - 1) / 2;
  return (x & y) == y;
}

int Dfs(int w, int cur, int lim) { // < lim
  if(cur >= lim) return 0;
  if(w == -1) return cur;
  if(cur & (1 << w)) return Dfs(w - 1, cur, lim);
  int nc = cur & ((1 << w + 1) - 1);
  if((cur ^ nc) + (1 << w + 1) <= lim) {
    int pc = __builtin_popcount(nc);
    if(pc == w + 1) return cur;
    else if(pc == w) {
      nc ^= ((1 << w + 1) - 1);
      return nc & -nc;
    }
    return 0;
  }
  return Dfs(w - 1, cur, lim) ^ Dfs(w - 1, cur ^ (1 << w), lim);
}

void Solve() {
  cin >> k >> m >> str;
  int all = count(ALL(str), '1'), ans = 0;
  for(int c = 0; c < all; c++) {
    if(S(all, c + 1)) ans ^= Dfs(29, c, m);
  }
  cout << ans << "\n";
}

int main() {
  // file();
  ios::sync_with_stdio(0), cin.tie(0);
  int T;
  cin >> T;
  while(T--) Solve();
  return 0;
}
posted @ 2025-01-19 08:45  CJzdc  阅读(185)  评论(0)    收藏  举报