[NOI2017]泳池
XXVIII.[NOI2017]泳池
常系数齐次线性递推的应用。
我们首先将问题转换为(面积小于等于的方案数)减去(面积小于等于的方案数)。
然后考虑两个东西分别DP。我们设当前考虑的是面积小于等于的情况。
我们设表示考虑一段长为的沙滩,其中前行都是安全的,而第行至少有一个危险的地方时,只存在面积小于等于的游泳池的概率。再设为一个格子是安全的概率,则为它是危险的概率。
注意,这里我们不考虑前行都是安全的概率——即,最终要乘上一个才是正确概率。
我们考虑再设表示前行都是安全的,第行往后可能出现危险格子,此时不存在面积大于的游泳池的方案数。
则我们应有
后面那个是第到第行全部安全的方案数。
它可以通过后缀和的形式求出:
下面我们考虑求出。我们考虑枚举第一个危险格子出现在哪里。则我们就有
因为前一半中,前行内必须没有任何东西;而后一半中,前行必须没有任何东西;中间的一个位置还必须是危险的。所以我们可以得到上面的转移式。
用来替代,就得到了
因为合法的前提,是,故实际上只有个是合法的;对于每个,都地转移,就可以在时间内完成。
(实际上,观察到转移是个卷积,也可以用NTT优化到,但是没有必要)
我们考虑设表示一个长度为的海滩不存在大于的游泳池的方案数。
我们再设表示一段长度为且第列内仅有最左端有一个危险格子的海滩的概率。则有。
则我们有
这是常系数齐次线性递推的形式!!!
先别急,我们再来考虑一下这一段的初始值:
然后就是模板了。这里多项式乘法和多项式取模都可选择直接暴力处理,因为只有。
#include<bits/stdc++.h>
using namespace std;
const int N=1<<20;
const int mod=998244353;
int ksm(int x,int y){
int z=1;
for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)z=1ll*z*x%mod;
return z;
}
int n,K,p,q,m,a[1010],tmp[2010],pov[1010];
void modulo(int *f){
for(int i=2*m-2;i>=m;i--){
int delta=f[i];
for(int j=0;j<=m;j++)(f[i-j]+=mod-1ll*delta*a[m-j]%mod)%=mod;
}
}
void times(int *f,int *g){
for(int i=0;i<=2*m-2;i++)tmp[i]=0;
for(int i=0;i<m;i++)for(int j=0;j<m;j++)(tmp[i+j]+=1ll*f[i]*g[j]%mod)%=mod;
for(int i=0;i<=2*m-2;i++)f[i]=tmp[i];
modulo(f);
}
int f[1010][1010],g[1010][1010],c[2010],d[2010],h[1010];
void KSM(){
memset(c,0,sizeof(c)),memset(d,0,sizeof(d));
d[0]=1,c[1]=1;
for(int y=n;y;y>>=1,times(c,c))if(y&1)times(d,c);
}
int solve(int ip){
m=ip;
memset(f,0,sizeof(f)),memset(g,0,sizeof(g));
for(int i=2;i<=m+2;i++)g[0][i]=1;
for(int i=1;i<=m;i++)for(int j=m/i+1;j>=2;j--){
for(int k=1;k<=i;k++)(f[i][j]+=1ll*g[k-1][j+1]*pov[k-1]%mod*q%mod*g[i-k][j]%mod)%=mod;
g[i][j]=(1ll*g[i][j+1]*pov[i]+f[i][j])%mod;
}
memset(a,0,sizeof(a));
for(int i=0;i<=m;i++)a[i+1]=1ll*q*g[i][2]%mod*pov[i]%mod;
m++;
memset(h,0,sizeof(h));
h[0]=1;
for(int i=1;i<m;i++){h[i]=1ll*g[i][2]*pov[i]%mod;for(int j=1;j<=i;j++)(h[i]+=1ll*h[i-j]*a[j]%mod)%=mod;}
reverse(a,a+m+1),a[m]=1;
for(int i=0;i<m;i++)a[i]=(mod-a[i])%mod;
KSM();
int res=0;
for(int i=0;i<m;i++)(res+=1ll*h[i]*d[i]%mod)%=mod;
return res;
}
int main(){
int X,Y;
scanf("%d%d%d%d",&n,&K,&X,&Y),p=1ll*X*ksm(Y,mod-2)%mod,q=(mod+1-p)%mod;
pov[0]=1;
for(int i=1;i<=K;i++)pov[i]=1ll*pov[i-1]*p%mod;
X=solve(K);
Y=solve(K-1);
printf("%d\n",(X-Y+mod)%mod);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?