Live2D

Solution -「ZJOI 2019」「洛谷 P5326」开关

Description

  Link.

  有 n 个开关,初始时所有开关的状态为 0。给定开关的目标状态 s1,s2,,sn。每次操作中会以正比于 pi 的概率拨动开关 i。求开关达到目标状态的期望操作次数,对 998244353 取模。

  n100p5×104

Solution

  不妨令 pi 为一次操作拨动 i 的概率。设 F(x) 为“i 次操作后开关是目标状态的概率”的 EGF,G(x) 为“i 次操作后回到全零状态的概率”的 EGF。考虑每个开关是否需要被拨动,得到

F(x)=i=1nepix+(1)siepix2

代入 si=0 (i=1,2,,n) 即得

G(x)=i=1nepix+epix2

  设 H(x) 为“i 次操作第一次使开关达到目标状态的概率”的 EGF。FH 的区别在于是否接受“多次回到目标状态”,而“回到目标状态”正对应着 G 的意义,它们可以建立等量关系

F=HG    H=FG1

故欲求期望 H(1),仅需求 (FG1)(1)

  设 u1i=2n[eix]F(x)v1i=2n[eix]G(x),则有

u1i=2n[eix]F(x)=[xi]j=1n(xpj+(1)sjxpj)=[xi1]j=1n(1+(1)sjx2pj)=[x1i]j=1n(1+(1)sjx2pj)

同理地,对于 vi

v1i=2n[eix]G(x)==[x1i]j=1n(1+x2pj)

在此基础上考虑所求答案:

H(x)=i2nu1ieixi2nv1ieix

注意 eix=EGF1,i,i2,,将其统一转为 OGF1,i,i2,=11ix,此时 H 的含义变为概率的 OGF。可以得到

H(x)=iu1i1ixiv1i1ix=(1x)iu1i1ix(1x)iv1i1ix=u0+i1u1i1x1ixv0+i1v1i1x1ix

s(x)=u0+i1u1i1x1ixt(x)=v0+i1v1i1x1ix。由于有 (1x1ix)(1)=11i,可知

s(1)=i1u1i1it(1)=i1v1i1i

而显然又有 s(1)=t(1)=u0=v0=1,则对于 H(1)

H(1)=s(1)t(1)s(1)t(1)t2(1)=s(1)t(1)=i1u1iv1ii1=i>0viuii

  故仅需求出 uivi,有意义的 i 仅有 O(p)(其中 p 即输入)个,背包一下,即可 O(nm) 求解。

Code

/* Clearink */

#include <cstdio>

#define rep( i, l, r ) for ( int i = l, rpbound##i = r; i <= rpbound##i; ++i )
#define per( i, r, l ) for ( int i = r, rpbound##i = l; i >= rpbound##i; --i )

const int MAXN = 100, MAXS = 5e4, MOD = 998244353;
int n, s[MAXN + 5], p[MAXN + 5], u[MAXS + 5], v[MAXS + 5];

inline int mul( const long long a, const int b ) { return a * b % MOD; }
inline int sub( int a, const int b ) { return ( a -= b ) < 0 ? a + MOD : a; }
inline int add( int a, const int b ) { return ( a += b ) < MOD ? a : a - MOD; }
inline int mpow( int a, int b ) {
	int ret = 1;
	for ( ; b; a = mul( a, a ), b >>= 1 ) ret = mul( ret, b & 1 ? a : 1 );
	return ret;
}

int main() {
	scanf( "%d", &n );
	rep ( i, 1, n ) scanf( "%d", &s[i] );
	int sp = 0;
	rep ( i, 1, n ) scanf( "%d", &p[i] ), sp += p[i];
	u[0] = v[0] = 1;
	rep ( i, 1, n ) per ( j, sp, p[i] ) {
		u[j] = ( s[i] ? sub : add )( u[j], u[j - p[i]] );
		v[j] = add( v[j], v[j - p[i]] );
	}
	// rep ( i, 0, sp ) printf( "%d%c", u[i], i ^ sp ? ' ' : '\n' );
	// rep ( i, 0, sp ) printf( "%d%c", v[i], i ^ sp ? ' ' : '\n' );
	int ans = 0;
	rep ( i, 1, sp ) {
		ans = add( ans, mul( mpow( i << 1, MOD - 2 ), sub( v[i], u[i] ) ) );
	}
	printf( "%d\n", mul( ans, sp ) );
	return 0;
}

posted @   Rainybunny  阅读(69)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示