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;
}
浙公网安备 33010602011771号