【Codeforces 850E】Random Elections(概率,FWT)
Description
有三個人 \(A, B, C\) 參加選舉。有 \(n\) 個選民,每個選民都有一個投票順序 \(b\),\(b(X)\) 表示 \(X\)(顯然 \(X\in\{A, B, C\}\))在其心中的排名。有 \(\{b(A), b(B), b(C)\}=\{1, 2, 3\}\)。
當 \(X\) 對決 \(Y\) 時會得到一個 01 序列 \(x_1, x_2, \cdots, x_n\),其中 \(x_i=[b(X)<b(Y)]\)。題目還會給定一個函式 \(f\),含義是若 \(f(x)=1\) 那麼 \(X\) 就贏了。函式 \(f\) 的 \(2^n\) 中 \(x\)
求在所有 \(b\) 的情況中,隨機選定一種後,\(A, B, C\) 三人中兩兩對決,其中有一個人贏了兩次的概率 \(\times 6^n\) 的結果(可以證明為整數)\(\bmod (10^9+7)\) 的值
Hint
\(1\le n\le 20\) 為什麼只有 20 個選民
Solution
根本沒思路/dk
由於三個人的地位相同,不妨假定 \(A\) 對決 \(B, C\),然後對求出的結果 \(\times 3\) 即為答案。
設 \(A\to B, A\to C\)
考慮第 \(i\) 個人的情況 \(a_i\) 和 \(b_i\),如果 \(a_i=0,b_i=1\),那麼說明 \(b(B) < b(A) < b(C)\),這三個人在 \(i\) 心中的順序已經被確定,同理 \(a_i=1, b_i=0\) 的情況也是如此。
反之,如果 \(a_i=b_i\),不論兩者是零是一,都只能說明 \(b(A)\) 同時小於或同時大於 \(b(B)\) 和 \(b(C)\),至於 \(b(B)\) 和 \(b(C)\) 的大小關係還沒有辦法確定,那麼還存在兩種可能的情況。
小結一下:如果 \(a_i \ne b_i\),那麼第 \(i\) 個人的 \(b\) 只有一種可能,反之就是兩種。對於序列 \(a, b\) 而言,對於所有 \(a_i\ne b_i\) 的人都快存在兩種情況,也就是一共 \(2^{\sum_i[a_i\ne b_i]}\) 中可能。
上面那個 \(\sum_i[a_i\ne b_i]\) 太難搞了,我們若是將 \(a, b\) 直接理解為兩個二進位制數,那麼就是 \(2^{n-\text{popcount}(a\oplus b)}\)(\(\oplus\) 為異或,\(\text{popcount}(x)\) 表示 \(x\) 中 1 的個數)。
最終統計答案的式子,就直接列舉 \(a, b\):
\[\sum_{a, b\in [0, 2^n)} 2^{n-\text{popcount}(a\oplus b)}\times f(a)\times f(b) \]改列舉 \(a, b\) 為列舉 \(a\oplus b\),就有:
\[\sum_{x\in [0, 2^n)} 2^{n-\text{popcount}(x)}\sum_{a\oplus b=x}f(a)\times f(b) \]設 \(g(x)=\sum_{a\oplus b=x}f(a)\times f(b)\),發現這是一個異或卷積,可以 FWT 搞出來 \(g\)。於是做完了。
複雜度 \(O(n\times 2^n)\)。
/*
* Author : _Wallace_
* Source : https://www.cnblogs.com/-Wallace-/
* Problem : Codeforces 850E Random Elections
*/
#include <cstdio>
const int mod = 1e9 + 7, inv2 = (mod + 1) >> 1;
inline int& reduce(int& x) { return x >= mod ? x -= mod : x; }
void fwt(int* f, int n, bool flag) {
for (int p = 2, q = 1; p <= (1 << n); q = (p <<= 1) >> 1)
for (int i = 0; i < (1 << n); i += p)
for (int x, y, j = i; j < i + q; j++) {
x = f[j], y = f[j + q], reduce(f[j] = x + y), reduce(f[j + q] = x - y + mod);
if (flag) f[j] = (f[j] * 1ll * inv2) % mod, f[j + q] = (f[j + q] * 1ll * inv2) % mod;
}
}
const int N = 20;
int n, f[1 << N];
signed main() {
static char s[1 << N]; scanf("%d%s", &n, s);
for (int i = 0; i < (1 << n); i++) f[i] = s[i] - '0';
fwt(f, n, 0);
for (int i = 0; i < (1 << n); i++)
f[i] = (f[i] * 1ll * f[i]) % mod;
fwt(f, n, 1);
int ans = 0;
for (int i = 0; i < (1 << n); i++)
reduce(ans += (f[i] * 1ll * (1 << (n - __builtin_popcount(i)))) % mod);
return printf("%lld\n", ans * 3ll % mod), 0;
}