【ZJOI2019】开关(FWT)(背包DP)

本文详细解析了一种基于异或卷积和快速沃尔什-哈达玛变换(FWT)的算法,用于解决开关问题中求解门开启期望步数的问题。通过构造生成函数和矩阵运算,提出了解决方案,并提供了具体实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


这道题目前知道的推导原理本质不同做法有两种,另一种是生成函数:here

题解:

p i p_i pi表示在单次操作中选择了第 i i i个开关的概率,设 U U U表示所有开关的 2 n 2^n 2n种不同状态的集合,设集合幂级数 F ( x ) = ∑ T ⊆ U f T x T F(x)=\sum_{T\subseteq U}f_Tx^T F(x)=TUfTxT,其中 f T f_T fT表示 s i = [ i ∈ T ] s_i=[i\in T] si=[iT]状态下打开门需要的期望步数。

对于 T ! = ∅ T!=\empty T!=,显然有 f T = 1 + ∑ i = 1 n p i f T ⊕ i f_T=1+\sum\limits_{i=1}^np_if_{T\oplus i} fT=1+i=1npifTi
对于 T = ∅ T=\empty T=,显然有 f ∅ = 0 f_\empty=0 f=0

发现转移是一个异或卷积,考虑构造 G ( x ) = ∑ i p i x i G(x)=\sum_{i}p_ix^i G(x)=ipixi,那么式子写出来就是 F = ∑ T ⊆ U x T + F ⋅ G + c x ∅ F=\sum_{T\subseteq U}x^T+F\cdot G+cx^\empty F=TUxT+FG+cx

这里 c c c是一个修正用的常数。

解异或卷积方程一般的通用方法就是FWT过去再IFWT回来。

先把式子写成 F ( 1 − G ) = ∑ T ⊆ U x T + c x ∅ F(1-G)=\sum_{T\subseteq U}x^T+cx^\empty F(1G)=TUxT+cx,然后FWT,设 F F F进行FWT后得到 F ^ \hat F F^,其各项系数为 f ^ \hat f f^ G G G同理。

考虑FWT后的形式,我们得到:

f ^ S ( 1 − g ^ S ) = ∑ T ( − 1 ) ∣ T ∩ S ∣ + c \hat f_S(1-\hat g_S)=\sum_{T}(-1)^{|T\cap S|}+c f^S(1g^S)=T(1)TS+c

S = ∅ S=\empty S=,显然有 g S = ∑ p i = 1 g_S=\sum p_i=1 gS=pi=1,则得到 c = − 2 n c=-2^n c=2n

s ! = ∅ s!=\empty s!=,得到 f ^ S = c 1 − g ^ s \hat f_S=\dfrac{c}{1-\hat g _s} f^S=1g^sc

再IFWT回去得到:

f S = 1 2 n ∑ T ( − 1 ) ∣ T ∩ S ∣ f ^ T = f ^ ∅ 2 n − ∑ T ≠ ∅ ( − 1 ) ∣ T ∩ S ∣ 1 1 − g ^ T f_S=\frac{1}{2^n}\sum_{T}(-1)^{|T\cap S|}\hat f_T=\frac{\hat f_{\empty}}{2^n}-\sum_{T\neq\empty}(-1)^{|T\cap S|}\frac{1}{1-\hat g_T} fS=2n1T(1)TSf^T=2nf^T=(1)TS1g^T1

S = ∅ S=\empty S=,由于 f ∅ = 0 f_\empty=0 f=0,得到 f ^ ∅ 2 n = ∑ T ≠ ∅ 1 1 − g ^ T \dfrac{\hat f_\empty}{2^n}=\sum_{T\neq \empty}\dfrac{1}{1-\hat g_T} 2nf^=T=1g^T1

则答案就是: f S = ∑ T ≠ ∅ ( 1 − ( − 1 ) ∣ T ∩ S ∣ ) 1 1 − g ^ T = ∑ T ≠ ∅ [ ∣ T ∩ S ∣ ≡ 1 ( m o d 2 ) ] 2 1 − g ^ T = ∑ T ≠ ∅ [ ∣ T ∩ S ∣ ≡ 1 ( m o d 2 ) ] 2 1 − ∑ i ∉ T p i + ∑ i ∈ T p i = ∑ T ≠ ∅ [ ∣ T ∩ S ∣ ≡ 1 ( m o d 2 ) ] 1 ∑ i ∈ T p i f_S=\sum_{T\neq\empty}(1-(-1)^{|T\cap S|})\frac{1}{1-\hat g_T}\\ =\sum_{T\neq \empty}[|T\cap S|\equiv 1\pmod 2]\frac{2}{1-\hat g_T}\\ =\sum_{T\neq \empty}[|T\cap S|\equiv 1\pmod 2]\frac{2}{1-\sum\limits_{i\notin T}p_i+\sum\limits_{i\in T}p_i}\\ =\sum_{T\neq \empty}[|T\cap S|\equiv 1\pmod 2]\frac{1}{\sum\limits_{i\in T}p_i} fS=T=(1(1)TS)1g^T1=T=[TS1(mod2)]1g^T2=T=[TS1(mod2)]1i/Tpi+iTpi2=T=[TS1(mod2)]iTpi1

发现 ∑ p \sum p p不是很大,直接背包即可,同时记录一维表示交的奇偶性最后直接算答案。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int mod=998244353;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}

cs int N=1e2+7,M=5e4+7;

int n;
int inv[M];
int s[N],p[N],a[M],b[M],P;

signed main(){
#ifdef zxyoi
	freopen("button.in","r",stdin);
#endif
	scanf("%d",&n);a[0]=1;
	for(int re i=1;i<=n;++i)scanf("%d",s+i);
	for(int re i=1;i<=n;++i)scanf("%d",p+i);
	for(int re i=1;i<=n;++i){
		int *a1=s[i]?b:a,*b1=s[i]?a:b,p=::p[i];
		for(int re j=P;~j;--j)
		Inc(a1[j+p],a[j]),Inc(b1[j+p],b[j]);
		P+=p; 
	}
	int ans=b[1];inv[1]=1;
	for(int re i=2;i<=P;++i){
		inv[i]=mul(mod-mod/i,inv[mod%i]);
		Inc(ans,mul(b[i],inv[i]));
	}
	cout<<mul(ans,P)<<"\n";
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值