P8967 追寻 | Pursuit of Dream 题解

P8967 追寻 | Pursuit of Dream

很久没做过这种酸爽的推式子题了。

首先可以确定的是,看似每个点的坐标的每个维度都是 107 级别的,实际上有用的点只有 k 个,剩下点之间的期望都是相同并且可计算的。

对于期望 DP,一般是逆推的方法。考虑设一个 fi 表示从第 i 个点出发到达终点的期望步数,其中特殊地定义 f0 表示 (0,0,,0) 这个点。由期望的线性性,这个期望分为三部分:直接到终点的期望、中间发生散入天际的期望。这两部分加和就是当前点的答案。最后的答案显然为 f0

直接到达终点

显然前者较为好算,我们先来计算前者,也就是直接到达终点的期望。

对于一个点 (a1,a2,,an),它到终点 (d1,d2,,dn) 显然需要 j=1n(djaj) 步,把这个值记作 si。注意到这样计算有意义当且仅当 djaj 时,因为我们只能往前走,于是但凡 dj<aj,这个点直接到达终点的期望就是 0

还有一点需要注意,我们要保证这 si 步都不会散入天际,这个概率是 (1p)si

想要走完这 si 步显然有 nsi 种走法,因为每一步都可以走在任意一个维度上。我们接下来需要求出合法的走法数量。注意到这个问题可以转化为多重集的排列数问题,即我们有一个集合 S,其中包含 d1a1x1d2a2x2、……、dnanxn,求 S 的全排列数。由公式可得,这个全排列数为 si!j=1n(djaj)!。也就是一共有这么多种合法的走法。最后我们得到直接到达终点的概率

qi=si!j=1n(djaj)!nsi×(1p)si

于是这部分的期望步数就是 qisi

出现散入天际

首先可以确定的是,不能使 fi 的式子里面出现 fj 之类的东西,那样的话就需要高斯消元,但复杂度我们显然无法承受。

但是注意到,不管从哪里散入天际,这个期望都是恒定的。也就是说,“散入天际之后到达终点” 这一部分的期望和 i 无关。于是考虑求出这一部分不变量,记作 g。容易得到

g=i=1kpipfi

到这一步,fi 的表达式还缺一部分,就是从 i 出发到达散入天际那个点的期望步数。直接求是不好求的,因为我们根本不知道走多少步会散入天际。考虑容斥,分别计算从当前点出发到处乱走然后遇到散入天际的期望,以及从当前点出发先走到终点再到处乱走然后散入天际的期望,二者相减就是答案。

前者的计算是一个很典的结论,如下:

i=1(1p)i1×i×p=1p

用求导可证明。后一部分的计算参考我们上文计算的结果,走到终点的期望步数是 qisi,之后的期望步数是 qi×1p=qip。所以从点 i 出发到达散入天际那个点的期望步数就是

1pqisiqip

于是我们得到了 fi 的最终表达式:

fi=qisi+(1qi)g+1pqisiqip=(1qi)g+(1qi)×1p=(1qi)(g+1p)

酸爽推式子

发现 fi 的这个式子中,qip 都是好求的,考虑解出 g

fi 代入 g=i=1kpipfi 中得到:

g=i=1kpip(1qi)(g+1p)=g(i=1kpip(1qi))+1p(i=1kpip(1qi))

再整理得:

(1i=1kpip(1qi))g=1p(i=1kpip(1qi))

于是我们只需知道 pi,p,qi 这三个量就可以解出 g,而这三个量都是好求的,于是解出 g 之后代入 f 的式子中,输出 f0 即可。

#include<bits/stdc++.h>
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define inv(x) power(x,MOD-2)
using namespace std;

char buf[1<<20],*p1=buf,*p2=buf;
int read(){
	int x=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x;
}
using ll=long long;
constexpr int MAXN=105,MAXK=10005,MAXV=1e7+5;
constexpr ll MOD=998244353,inv8=205817851;
int n,k;
ll d[MAXN],a[MAXK][MAXN],p[MAXK],P,invP,s[MAXK],q[MAXK];
ll fac[MAXV];

ll power(ll a,ll b){
	ll res=1;
	for(;b;a=a*a%MOD,b>>=1)if(b&1)res=res*a%MOD;
	return res;
}
void init(){
	fac[0]=1;
	for(int i=1;i<MAXV;i++) fac[i]=fac[i-1]*i%MOD;
}
void add(ll&x,ll y){
	x=(x+y>=MOD?x+y-MOD:x+y);
}

int main(){
	init();
	n=read(),k=read();
	for(int i=1;i<=n;i++) d[i]=read(),s[0]+=d[i];
	for(int i=1;i<=k;i++){
		for(int j=1;j<=n;j++){
			a[i][j]=read();
			if(d[j]<a[i][j]) s[i]=-1;
			else if(~s[i]) s[i]+=d[j]-a[i][j];
		}
		p[i]=inv8*read()%MOD;
		add(P,p[i]);
	}
	invP=inv(P);
	ll res1,res2=0,g;
	for(int i=0;i<=k;i++){
		if(~s[i]){
			q[i]=fac[s[i]]*power((1-P+MOD)%MOD,s[i])%MOD*inv(power(n,s[i]))%MOD;
			for(int j=1;j<=n;j++) q[i]=q[i]*inv(fac[d[j]-a[i][j]])%MOD;
		}
		add(res2,p[i]*invP%MOD*((1-q[i]+MOD)%MOD)%MOD);
	}
	res1=(1-res2+MOD)%MOD;
	res2=res2*invP%MOD;
	g=res2*inv(res1)%MOD;
	printf("%lld\n",(1-q[0]+MOD)%MOD*((g+invP)%MOD)%MOD);
	return 0;
}
posted @   Laoshan_PLUS  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示