【ZJOI2019】开关(生成函数)(单位根反演)(背包DP)

传送门


目前知道推导原理不同的做法有两种,另一种是基于集合的FWT变换解一个集合幂级数:here

题解:

p i p_i pi表示一次操作中选择了第 i i i个开关的概率。

首先设 F ( x ) F(x) F(x)表示操作了 i i i次之后的状态是开门状态的概率的 E G F EGF EGF注意我们并不要求这个时刻是第一次达到开门状态,考虑单位根反演,得到:

F ( x ) = ∏ i = 1 n e p i x + ( − 1 ) s i e − p i x 2 F(x)=\prod_{i=1}^{n}\frac{e^{p_ix}+(-1)^{s_i}e^{-p_ix}}{2} F(x)=i=1n2epix+(1)siepix

G ( x ) G(x) G(x)表示操作了 i i i次之后回到原状态的概率的 E G F EGF EGF,同样,我们不考虑中间是否已经达到过合法状态,显然有 G ( x ) = ∏ i = 1 n e p i x + e − p i x 2 G(x)=\prod_{i=1}^n\frac{e^{p_ix}+e^{-p_ix}}{2} G(x)=i=1n2epix+epix

f , g f,g f,g分别是 F , G F,G F,G O G F OGF OGF,设 h h h是第一次到达开门状态操作了 i i i次的概率,显然答案就是 h ′ ( 1 ) h'(1) h(1)

枚举过程中第一次到达开门状态的概率,我们得到 h ⋅ g = f h\cdot g=f hg=f,即 h = f / g h=f/g h=f/g,那么 h ′ = f ′ g − g ′ f g 2 h'=\frac{f'g-g'f}{g^2} h=g2fggf

考虑 E G F EGF EGF O G F OGF OGF,我们知道对于同一个数列的 E G F EGF EGF O G F OGF OGF,有 f ( x ) = ∫ F ( x t ) e − t d t f(x)=\int F(xt)e^{-t}\mathrm{d}t f(x)=F(xt)etdt

对于这道题我们尝试把 F F F写成这种形式: F ( x ) = ∑ − 1 ≤ i ≤ 1 a i e i x F(x)=\sum_{-1\leq i\leq 1}a_ie^{ix} F(x)=1i1aieix,注意这里 i i i是一个实数,这里显然只有有限个 a i a_i ai 0 0 0。则 f = ∑ − 1 ≤ i ≤ 1 a i 1 − i x f=\sum_{-1\leq i\leq 1}\frac{a_i}{1-ix} f=1i11ixai,同理设 g = ∑ − 1 ≤ i ≤ i b i 1 − i x g=\sum_{-1\leq i\leq i}\frac{b_i}{1-ix} g=1ii1ixbi,我们可以背包算 a , b a,b a,b

考虑 h ′ = f ′ g − g ′ f g 2 h'=\frac{f'g-g'f}{g^2} h=g2fggf,发现上下在 x = 1 x=1 x=1处不收敛很方,那么我们用 h = f ( 1 − x ) g ( 1 − x ) h=\frac{f(1-x)}{g(1-x)} h=g(1x)f(1x)来推导试一试,注意一下由于 f , g f,g f,g是各种的情况下概率的生成函数,所以 f ( 1 ) = g ( 1 ) = 1 f(1)=g(1)=1 f(1)=g(1)=1,可以得到 h ′ = ( f ( 1 − x ) ) ′ − ( g ( 1 − x ) ) ′ b 1 h'=\frac{(f(1-x))'-(g(1-x))'}{b_1} h=b1(f(1x))(g(1x))

稍微推导一下知道一定有 b 1 = 1 2 n b_1=\frac{1}{2^n} b1=2n1,然后我们考虑 f ( 1 − x ) f(1-x) f(1x)的每一项,显然 a 1 1 − x ( 1 − x ) \frac{a_1}{1-x}(1-x) 1xa1(1x)项求导为 0 0 0,剩下的项考虑 ( 1 − x 1 − i x ) ′ ∣ x = 1 = 1 i − 1 (\frac{1-x}{1-ix})'|_{x=1}=\frac{1}{i-1} (1ix1x)x=1=i11

然后就可以做了,注意到背包出来值非零的位置的奇偶性相同,可以优化一下。

但是你说我要是没有想到上下乘上一个 1 − x 1-x 1x,或者我并不是很会推微积分怎么办,拿草稿纸推还被rqy给Diss了

考虑对于 f , g f,g f,g的表达式暴力通分,得到 f ( x ) = A ( x ) B ( x ) , g ( x ) = C ( x ) D ( x ) f(x)=\frac{A(x)}{B(x)},g(x)=\frac{C(x)}{D(x)} f(x)=B(x)A(x),g(x)=D(x)C(x)显然分母 B ( x ) , D ( x ) B(x),D(x) B(x),D(x)是相同的,所以我们要求的是 ( A ( 1 ) C ( 1 ) ) ′ = A ( 1 ) ′ C ( 1 ) − C ( 1 ) ′ A ( 1 ) C ( 1 ) 2 (\frac{A(1)}{C(1)})'=\frac{A(1)'C(1)-C(1)'A(1)}{C(1)^2} (C(1)A(1))=C(1)2A(1)C(1)C(1)A(1)

好了现在全部收敛了,维护一下 ( 1 − i x ) (1-ix) (1ix)的前缀后缀积就可以做了。


代码:

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

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;}

cs int N=1e2+7,M=5e4+7;
int n;
int inv[M];
int a[M],b[M],P;

inline int der1(int *f){
	int ans=0;
	for(int re i=0;i<P;++i)Inc(ans,mul(mul(mod>>1,inv[P-i]),f[i]));
	return ans;
}

int s[N],p[N];
int main(){
#ifdef zxyoi
	freopen("button.in","r",stdin);
#endif
	scanf("%d",&n);a[0]=b[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 p=::p[i],s=::s[i];P+=p;
		if(s==1){
			for(int re j=P;j>=p;--j)a[j]=dec(a[j-p],a[j]);
			for(int re j=p-1;~j;--j)a[j]=a[j]?mod-a[j]:0;
		}else for(int re j=P;j>=p;--j)Inc(a[j],a[j-p]);
		for(int re j=P;j>=p;--j)Inc(b[j],b[j-p]);
	}
	inv[1]=1;
	for(int re i=2;i<=P;++i)inv[i]=mul(mod-mod/i,inv[mod%i]);
	std::cout<<mul(P,dec(der1(a),der1(b)))<<"\n";
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值