@loj


@description@

九条可怜是一个贪玩的女孩子。

这天,她和她的好朋友法海哥哥去玩密室逃脱。在他们面前的是 (n) 个开关,开始每个开关都是关闭的状态。要通过这关,必须要让开关达到指定的状态。目标状态由一个长度为 (n)(01) 数组 (s) 给出,(s_i = 0) 表示第 (i) 个开关在最后需要是关着的,(s_i = 1) 表示第 (i) 个开关在最后需要被打开。

然而作为闯关者,可怜和法海并不知道 (s)。因此他们决定采用一个比较稳妥的方法:瞎按。他们根据开关的外形、位置,通过一些玄学的方法给每一个开关赋予了一个值 (p_i(p_i>0))。每一次,他们会以正比于 (p_i) 的概率(第 (i) 个开关被选中的概率是 (frac{p_i}{sum_{j=1}^{n} p_j}))选择并按下一个开关。开关被按下后,状态会被反转,即开变关,关变开。注意每一轮的选择都是完全独立的

在按开关的过程中,一旦当前开关的状态达到了 (s),那么可怜和法海面前的门就会打开,他们会马上停止按开关的过程并前往下一关。作为一名欧皇,可怜在按了 (sum_{i=1}^{n}s_i) 次后,就打开了大门。为了感受一下自己的运气是多么的好,可怜想要让你帮她计算一下,用这种随机按的方式,期望需要按多少次开关才能通过这一关。

原题传送门。

@solution@

本题和 CTS2019 - 珍珠 类似,既可以用生成函数解,也可以用 fwt 解。
生成函数解法详见 粉兔大佬的blog(orz)。

我们考虑从期望 dp 的角度分析。记 (f_S) 表示当前开关状态为 (S),到达终态的期望步数。
不妨记 (q_i=frac{p_i}{sum p_i}),则有转移:

[egin{cases} f_S &= 1+sum_{i=1}^{n}f_{S m{xor} 2^i} imes q_i\ f_0 &= 0 end{cases} ]

转移可以写成卷积形式。不过由于 (f_0) 不满足转移,需要加上修正量 (k)。得到卷积式:

[Fotimes Q + I = F+k ]

其中 (I) 表示全 1 的序列。

上式可以进一步化成 (Fotimes (Q - 1) = k-I)。对其进行 fwt 变换,得到 (fwt(F) imes fwt(Q-1)=fwt(k-I))

我们需要知道 fwt 变换的本质在干什么:(fwt(F)_S = sum_{T}(-1)^{|Scap T|}f_T)

然后得到如下式子:

[egin{aligned} fwt(Q-1)_S&=sum_{i otin S}q_i-sum_{iin S}q_i-1\ fwt(k-I)_S&=-sum_{Tsubseteq S}(-1)^{|T|} imes 2^{n-|S|}+k \ &=-2^{n-|S|} imessum_{i=0}^{|S|}{|S|choose i}(-1)^i+k \ &=k-[S = 0] imes 2^n\ end{aligned} ]

注意到当 (S = empty)(fwt(Q-1)_0 = 0),那么 (fwt(k-I)_0 = k-2^n=0)
也就是说可以解得 (k = 2^n)

接下来求 (fwt(F)_S =frac{fwt(k-I)}{fwt(Q-1)}),注意此时有 (S ot = empty)

[egin{aligned} fwt(F)_S=frac{2^n}{sum_{i otin S}q_i-sum_{iin S}q_i-1} end{aligned} ]

类比得到 (ifwt) 的本质为 (ifwt(F)_S = frac{1}{2^n}sum_{T}(-1)^{|Scap T|}f_T)。则:

[egin{aligned} F_S &= frac{1}{2^n}(sum_{T ot =empty}frac{(-1)^{|Scap T|} imes 2^n}{sum_{i otin T}q_i-sum_{iin T}q_i-1} + fwt(F)_0) \ &= sum_{T ot =empty}frac{(-1)^{|Scap T|} imessum p_i}{sum_{i otin T}p_i-sum_{iin T}p_i-sum p_i} + frac{fwt(F)_0}{2^n} \ &= -sum_{T ot =empty}frac{(-1)^{|Scap T|} imessum p_i}{2sum_{iin T}p_i} + frac{fwt(F)_0}{2^n} end{aligned} ]

如果已知 (frac{fwt(F)_0}{2^n}),以 (sum_{iin T}p_i) 为状态作背包 dp 就可以 (O(nsum p)) 算出 (F) 的某一项了。

注意到还有条件 (F_0 = 0),可以通过这个条件解出 (frac{fwt(F)_0}{2^n})

@accepted code@

#include <cstdio>
#include <algorithm>
using namespace std;

const int MOD = 998244353;
const int MAX = 50000;

inline int add(int x, int y) {x += y; return x >= MOD ? x - MOD : x;}
inline int sub(int x, int y) {x -= y; return x < 0 ? x + MOD : x;}
inline int mul(int x, int y) {return (int)(1LL * x * y % MOD);}

int pow_mod(int b, int p) {
	int ret = 1;
	for(int i=p;i&1;i>>=1,b=mul(b,b))
		if( i & 1 ) ret = mul(ret, b);
	return ret;
}

int inv[MAX + 5];
void init() {
	inv[1] = 1;
	for(int i=2;i<=MAX;i++)
		inv[i] = sub(0, mul(MOD / i, inv[MOD % i]));
}

int f[2][MAX + 5], a[105], p[105], sump, n;
int solve() {
	for(int i=0;i<=MAX;i++)
		f[0][i] = f[1][i] = 0;
	f[1][0] = 1;
	for(int i=1;i<=n;i++) {
		for(int j=MAX;j>=p[i];j--)
			for(int o=0;o<=1;o++)
				f[o][j] = add(f[o][j], f[o^a[i]][j - p[i]]);
	}
	
	int ans = 0;
	for(int i=1;i<=MAX;i++) {
		ans = add(ans, mul(f[0][i], mul(inv[2], inv[i])));
		ans = sub(ans, mul(f[1][i], mul(inv[2], inv[i])));
	}
	return mul(ans, sump);
}

int s[105];
int main() {
	init(), scanf("%d", &n);
	for(int i=1;i<=n;i++) scanf("%d", &s[i]);
	for(int i=1;i<=n;i++) scanf("%d", &p[i]), sump = add(sump, p[i]);
	
	int A = solve();
	for(int i=1;i<=n;i++) a[i] = s[i];
	int B = solve();
	printf("%d
", sub(B, A));
}

@details@

感觉这个推导方法比用EGF快不少。

类似的题。。。可能 agc034f 也算这一类型的题目?

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/13086827.html