Atcoder ARC165D Xor Sum 5

rizynvu發表於2024-03-18

考慮到這個最終的答案是 \(\oplus\),所以只需要考慮每種排列出現的次數的奇偶,如果為奇再計算貢獻即可。

考慮什麼情況下出現次數為奇。
令每個數出現的個數為 \(c_{1\sim n}\),方案數即為 \(\dbinom{k}{c_1, c_2, \cdots, c_n} = \prod_{i = 1}^n \dbinom{k - \sum\limits_{j = 1}^{i - 1} c_j}{c_i}\)
如果 \(\bmod 2 = 1\) 那麼就說明 \(\dbinom{k - \sum\limits_{j = 1}^{i - 1} c_j}{c_i}\bmod 2 = 1(1\le i\le n)\),由 \(\text{Lucas}\) 定理可以得到二進位制意義下 \(c_i\subseteq (k - \sum\limits_{j = 1}^{i - 1} c_j)\)
證明就是考慮拆二進位制位,因為 \(\binom{0}{0} = \binom{1}{1} = \binom{1}{0} = 1, \binom{0}{1} = 0\),所以如果 \(\bmod 2 = 1\) 需要每一位都滿足是前 \(3\) 種,也就是對應的子集關係。

那麼整理一下就有 \(c_i\operatorname{bitand}c_j = 0(1\le i < j\le n), c_1\operatorname{bitor} c_2 \operatorname{bitor}\cdots \operatorname{bitor} c_n = k\)

對於答案,可以考慮拆位,對每一位依次來考慮為 \(0\) 還是 \(1\)

因為能發現值域 \(V\le 1000\),考慮直接 \(\text{DP}\),設 \(f_{w, i}\) 為只考慮到第 \(w\) 位,到這一位的和為 \(j\) 的方案數為奇還是偶。
那麼轉移分為兩種,如果下一位 \(k\)\(0\),就選不了數,就直接折半由 \(i\) 轉移到 \(\lfloor\frac{i}{2}\rfloor\)
否則這一位需要選一個 \(a_j\) 出來,就會轉移到 \(\lfloor\frac{i}{2}\rfloor + a_j\)

然後考慮算這一位是不是 \(1\)
首先因為只考慮了前 \(w\) 位,若還有 \(c\) 位沒考慮,那麼方案數應該乘上 \(n^{c}\),當 \(n\bmod 2 = 0\land c > 0\) 時肯定為偶,直接跳過。
否則考慮找出 \(f_{w, i} = 1\)\(i\),把這些 \(\oplus\) 起來,如果最低位為 \(1\) 就說明第 \(w\) 位為 \(1\)

時間複雜度 \(O(nV\log k)\)\(V\) 為值域。

程式碼
#include<bits/stdc++.h>
using i64 = long long;
const int maxn = 1e3 + 10;
int n;
int a[maxn];
int is1[52];
int f[52][maxn * 2];
int main() {
    i64 k; scanf("%d%lld", &n, &k);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 0; i < 50; i++) is1[i] = (k >> i & 1ll);
    int c = 0;
    for (int i = 0; i < 50; i++) c += is1[i];
    i64 ans = 0;
    if (is1[0]) {
        for (int i = 1; i <= n; i++) f[0][a[i]] ^= 1;
    } else f[0][0] = 1;
    for (int i = 0; i < 50; i++) {
        if (is1[i]) c--;
        if ((n & 1) || ! c) {
            int t = 0;
            for (int j = 0; j <= 2000; j++) f[i][j] && (t ^= j);
            if (t & 1) ans |= 1ll << i;
        }
        if (! is1[i + 1]) {
            for (int j = 0; j <= 2000; j++) f[i + 1][j >> 1] ^= f[i][j];
        } else {
            for (int j = 0; j <= 2000; j++) for (int p = 1; p <= n; p++) f[i + 1][(j >> 1) + a[p]] ^= f[i][j];
        }
    }
    printf("%lld\n", ans);
    return 0;
}

相關文章