「ZJOI2019」开关 (概率期望+FWT)
Address
Solution
首先把目标状态和初始状态互换,答案显然不变。
考虑状压 \(\text{dp}\),现在记 \(F[S]\) 表示初始状态为 \(S\) 时,期望多少步到达目标状态。
接下来假设 \(\sum_{i=1}^np_i=1\),如果输入不满足,把 \(p_i\) 全部除以 \(\sum p_i\) 即可。
那么有:$$F[\emptyset]=0$$ $$S\ne\emptyset,F[S]=\sum_ip_i×(F[S\oplus \left{i\right}]+1)$$ 其中 \(\oplus\) 表示异或,\(\left\{i\right\}\) 表示只含 \(i\) 这一个元素的集合。
记 \(G[\left\{i\right\}]=p_i\),当 \(|S|\ne 1\) 时,\(G[S]=0\)。
定义两个数组的异或卷积:如果 \(C=A\times B\),那么有:$$\forall S,C[S]=\sum_U\sum_V[U\oplus
V=S]A[U]×B[V]$$定义两个数组的点积:如果 \(C=A\cdot B\),那么有 $$\forall S,C[S]=A[S]\times B[S]$$
定义两个数组相加:如果 \(C=A+B\),那么有 $$\forall S,C[S]=A[S]+B[S]$$
记一个全 \(1\) 数组 \(H\),即 \(\forall S,H[S]=1\)。
定义数组 \(R\),其中 \(R[\emptyset]=c\),\(\forall S\ne\emptyset,R[S]=0\)。\(c\) 的值暂时还不知道,先用字母表示。
那么可以得到:$$F=H+F×G+R$$ \(R\) 的存在是由于 \(S=\emptyset\) 和 \(S\ne \emptyset\) 时,\(F[S]\) 的递推式不一样。
定义变换 \(\tilde C\)(这玩意就是 \(FWT\) 变换,已经会的自觉跳过),其中 $$\tilde
C[S]=\sum_T(-1)^{|S\cap T|}C[T]$$根据上式显然有:如果 \(C=A+B\),那么 \(\tilde C=\tilde A+\tilde B\)。
而且可以得到 $$C[S]=\frac{1}{2n}\sum_T(-1)\tilde C[T]$$
其中 \(n\) 是 \(S\) 的二进制位数,考虑证明上式。
相当于证明:$$C[S]=\frac{1}{2n}\sum_A\sum_B(-1)(-1)^{|A\cap
B|}C[B]$$当 \(S=B\) 时,上式化为 $$C[S]=\frac{1}{2n}\sum_A(-1)C[B]$$
即 $$C[S]=\frac{1}{2^n}\sum_AC[B]$$
显然成立。
当 \(S\ne B\) 时,我们随便抓一个只在 \(S,B\) 其中一者中出现的元素 \(i\)。对于某个集合 \(A'\),如果 \(i\notin A'\),那么 \(A=A'\) 和 \(A=A'+\left\{i\right\}\) 的贡献互为相反数,因为:$$(-1)^{|S\cap
A'|}(-1)^{|A'\cap B|}=-(-1)^{|S\cap
(A'+\left{i\right})|}(-1)^{|(A'+\left{i\right})\cap B|}$$而这样的 \(A'\) 正好有 \(2^{n-1}\) 个,也就是说所有的 \(A\) 可以两两配对,贡献全部抵消,那么 \(S\ne B\)
的时候就有:$$\frac{1}{2n}\sum_A(-1)(-1)^{|A\cap B|}=0$$证毕。
有个性质:若 \(C=A\times B\),那么 \(\tilde C=\tilde A\cdot \tilde B\),证明如下:
\[C[S]=\sum_L\sum_R[L\oplus R\oplus S=0]A[L]B[R] \]因为 \(\sum_T(-1)^{|S\cap T|}=2^n[S=\emptyset]\),所以:
\[C[S]=\frac{1}{2^n}\sum_L\sum_R\sum_T(-1)^{|T\cap(L\oplus R\oplus S)|}A[L]B[R]\]接着,显然有 $$T\cap(L\oplus R\oplus S)=(T\cap L)\oplus(T\cap R)\oplus(T\cap
S)$$那么 $$(-1)^{|T\cap(L\oplus R\oplus S)|}=(-1)^{|T\cap L|+|T\cap
R|+|T\cap S|}$$上式可以理解为:若 \(z=x\oplus y\),那么 \(|z|\&1=(|x|+|y|)\&1\),因为异或相当于二进制下的不进位加法,所以
\(x\oplus y\) 时,\(x,y\) 二进制中的 \(1\) 只会两两一起消掉,\(1\) 的总数的奇偶性不会变。所以
\[C[S]=\frac{1}{2^n}\sum_L\sum_R\sum_T(-1)^{|T\cap L|}(-1)^{|T\cap R|}(-1)^{|T\cap S|}A[L]B[R]\]\[C[S]=(\frac{1}{2^n}\sum_T(-1)^{|T\cap S|})(\sum_L(-1)^{|T\cap L|}A[L])(\sum_R(-1)^{|T\cap R|}B[R])\]\[C[S]=(\frac{1}{2^n}\sum_T(-1)^{|T\cap S|})\tilde A[T]\times\tilde B[T]\]根据 $$C[S]=\frac{1}{2n}\sum_T(-1)\tilde C[T]$$
可得 $$\tilde C[T]=\tilde A[T]\times\tilde B[T]$$
证毕。
让我们回到:$$F=H+F×G+R$$
移项,得到 $$F(1-G)=H+R$$
令 \(A=F(1-G)\),则 $$\tilde A[U]=\tilde F[U]×(1-\tilde G[U])$$
令 \(A=H+R\),则 $$\tilde A[U]=\sum_T(-1)^{|T\cap U|}+c$$
于是 $$\tilde F[U]×(1-\tilde G[U])=\sum_T(-1)^{|T\cap U|}+c$$
当 \(U=\emptyset\) 时,有:$$\tilde G[U]=\sum p_i=1$$
代入上式可得 \(c=-2^n\)。
当 \(U\ne\emptyset\) 时,有:$$\tilde G[U]=\sum_{i}(-1)^{[i∈U]}p_i$$
此时 \(\tilde G[U]<1\),代入上式可得:
根据式子 $$\sum_T(-1)^{|S\cap T|}=2^n[S=\emptyset]$$
可得 $$\sum_T\tilde F[T]=2^nF[\emptyset]=0$$
那么 $$\tilde F[\emptyset]=-\sum_{T\ne \emptyset}\tilde F[T]=\sum_{T\ne \emptyset}\frac{2^n}{1-\tilde G[T]}$$
现在可以由 \(\tilde F\) 变回 \(F\) 了,即 $$F[S]=\frac{1}{2n}\sum_T(-1)\tilde F[T]$$
根据上面 \(\tilde F\) 的表达式,可得 $$F[S]=\sum_{T}(1-(-1)^{|S\cap T|})\frac{1}{1-\tilde G[T]}$$
根据 \(\tilde G\) 的表达式可得 $$1-\tilde G[T]=\sum_{i∈T}2×p_i$$
那么 $$F[S]=\sum_{T}(|S\cap T|&1)\frac{1}{\sum_{i∈T}p_i}$$
记 \(dp[i][j][k]\) 表示前 \(i\) 个数的子集,和 \(S\) 交集大小的奇偶性为 \(k\),子集的 \(p\) 之和为 \(j\),满足这些条件的子集个数。
那么 $$ans=\sum_j\frac{dp[n][j][1]}{j}$$
但是如果 \(p_i\) 全部除以 \(\sum_{i=1}^np_i\),\(p_i\) 就不是整数了,无法 \(dp\)。所以一开始不要把 \(p_i\) 除以 \(\sum p_i\),直接 \(dp\)。 最后再把答案乘上 \(\sum p_i\) 即可。
时间复杂度 \(\mathcal O(n\sum p_i)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
template <class t>
inline void read(t & res)
{
char ch;
while (ch = getchar(), !isdigit(ch));
res = ch ^ 48;
while (ch = getchar(), isdigit(ch))
res = res * 10 + (ch ^ 48);
}
const int e = 105, o = 5e4 + 5, mod = 998244353;
int n, s[e], p[e], sum, inv[o], f[e][o][2], ans;
inline void add(int &x, int y)
{
(x += y) >= mod && (x -= mod);
}
int main()
{
read(n);
int i, j;
for (i = 1; i <= n; i++) read(s[i]);
for (i = 1; i <= n; i++) read(p[i]), sum += p[i];
inv[1] = 1;
for (i = 2; i <= sum; i++) inv[i] = (ll)(mod - mod / i) * inv[mod % i] % mod;
f[0][0][0] = 1;
for (i = 1; i <= n; i++)
for (j = 0; j <= sum; j++)
{
f[i][j][0] = f[i - 1][j][0];
f[i][j][1] = f[i - 1][j][1];
if (j >= p[i])
{
add(f[i][j][0], f[i - 1][j - p[i]][s[i]]);
add(f[i][j][1], f[i - 1][j - p[i]][s[i] ^ 1]);
}
}
for (i = 1; i <= sum; i++) ans = (ans + (ll)inv[i] * f[n][i][1]) % mod;
ans = (ll)ans * sum % mod;
cout << ans << endl;
return 0;
}