泳池

泳池

题解

做这题时自闭了一个下午。还在自闭中。

看到这个图的第一眼时,笔者还未反应过来这题到底是什么。可能是笔者语文不好

先讲一下题意,我们只能在一个每个格子(其危险的概率为p),我们只能从底层开始画一个全是安全的矩阵面积为k时的概率。

我们发现,直接求这个概率不大方便,我们可以分别求出小于等于k的概率与小于等于k-1的概率,将它们相减即可。

可我们到底应该怎么做呢?考虑dp。

f_{i}为底面宽为i时的答案,dp_{i,j}为一个宽为i,高度为j的矩形,其中只有第j行有可能有危险。

状态转移方程式为:f_{n}= \sum_{i=0}^{k}f_{n-i-1}\sum_{j=2}^{\left \lfloor \frac{k}{i}+1 \right \rfloor}dp_{i,j}

想后缀和一样,我们令g_{i,j}= \sum_{l\geq j}dp_{i,l},那么f_{n}= \sum_{i=0}^{k}f_{n-i-1}\cdot g_{i,2}

好像还是会超时,貌似还可以用多项式取模的方法来优化。

dp_{i,j}= \left [ i\cdot (j-1) \leq k \right ](1-p)p^{j-1}\sum_{l=1}^{i}(\sum_{q>j}dp_{l-1,q}\codt\sum_{q \leq j}dp_{i-l,q})

这样,就可以用O\left ( k^{2}log_{k} \right )卡过了,貌似还可以用FFT优化,不过笔者懒得打了。谁叫笔者是一个蒟蒻。

源码

巨佬没兴趣可以跳过。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#define MAXN 1005
using namespace std;
typedef long long LL;
#define int LL
const int mo=998244353;
#define gc() getchar()
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=gc();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
	x*=f;
}
int qkpow(int a,int s){
	int t=1;
	while(s){
		if(s&1)t=t*a%mo;
		a=a*a%mo;s>>=1;
	}
	return t;
}

int n,m,a,b,p,q;
int dp[MAXN][MAXN],g[MAXN][MAXN];
int powp[MAXN],g1[MAXN],f[MAXN<<1];
int res[MAXN<<2],tmp[MAXN<<2],x[MAXN<<2];
int solve(int k){
	memset(dp,0,sizeof(dp));memset(g,0,sizeof(g));
	memset(g1,0,sizeof(g1));memset(f,0,sizeof(f));
	memset(res,0,sizeof(res));memset(x,0,sizeof(x));
	for(int i=1;i<=k+2;i++)dp[0][i]=g[0][i]=1;
	for(int i=1;i<=k;i++)
		for(int j=k/i+1;j>1;j--){
			for(int l=1;l<=i;l++)
				dp[i][j]=(dp[i][j]+g[l-1][j+1]*g[i-l][j]%mo*powp[l-1]%mo*q%mo)%mo;
			g[i][j]=(g[i][j+1]*powp[i]+dp[i][j])%mo;
		}
	for(int i=0;i<=k;i++)g1[i+1]=q*g[i][2]%mo*powp[i]%mo;f[0]=1;
	for(int i=1;i<=k;i++){
		f[i]=g[i][2]*powp[i]%mo;
		for(int j=1;j<=i;j++)
			f[i]=(f[i]+g1[j]*f[i-j]%mo)%mo;
	}
	k++;
	for(int i=k;i<=k<<1;i++)
		for(int j=1;j<=k;j++)
			f[i]=(f[i]+g1[j]*f[i-j]%mo)%mo;
	if(n<=k)return f[n];
	int lim=1,L=0;
	res[0]=1;x[1]=1;
	for(int i=n-k;i>0;i>>=1){
		if(i&1){
			for(int i=0;i<=L+lim;i++)tmp[i]=0;
			for(int i=0;i<=L;i++)
				for(int j=0;j<=lim;j++)
					tmp[i+j]=(tmp[i+j]+res[i]*x[j]%mo)%mo;
			L+=lim;
			for(int i=L;i>=k;i--)
				for(int j=1;j<=k;j++)
					tmp[i-j]=(tmp[i-j]+tmp[i]*g1[j])%mo;
			L=min(L,k-1);
			for(int i=0;i<=L;i++)res[i]=tmp[i];
		}
		for(int i=0;i<=2*lim;i++)tmp[i]=0;
		for(int i=0;i<=lim;i++)
			for(int j=0;j<=lim;j++)
				tmp[i+j]=(tmp[i+j]+x[i]*x[j]%mo)%mo;
		lim<<=1;
		for(int i=lim;i>=k;i--)
			for (int j=1;j<=k;j++)
				tmp[i-j]=(tmp[i-j]+g1[j]*tmp[i]%mo)%mo;
		lim=min(lim,k-1);
		for(int i=0;i<=lim;i++)x[i]=tmp[i];
	}
	int ans=0;
	for(int i=0;i<k;i++)ans=(ans+res[i]*f[i+k]%mo)%mo;
	return ans;
}
signed main(){
	read(n);read(m);read(a);read(b);
	p=a*qkpow(b,mo-2)%mo;q=(mo+1-p)%mo;
	powp[0]=1;for(int i=1;i<=m;i++)powp[i]=powp[i-1]*p%mo;
	printf("%lld\n",(solve(m)-solve(m-1)+mo)%mo);
    return 0;
}

谢谢!!!

发布了117 篇原创文章 · 获赞 154 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Tan_tan_tann/article/details/103672520