Noi2017 泳池
首先考虑dp。
不难发现我们大概是对于每一个高度不能有连续的一段长度,并且在下面都安全的时候才计入限制。
\(f[i][j]\)表示长度为i的泳池从高度j开始(i,j)的矩形默认安全,满足最大矩形小于k的概率.
转移相当于做一个类似背包的东西。
然后发现\(j>0\)的时候\(i\le 1000\)(否则dp值为0),然后因为要枚举上一层选择了多少,这个最多到1000。
则,枚举上一个0的位置
\[f[i][0]=\sum f[j][0] * (f[i-j-1][1] * p^{i-j-1}*(1-p))
\]
线性递推就没了。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
inline int add(int a,int b){a+=b;return a>=mod?a-mod:a;}
inline int sub(int a,int b){a-=b;return a<0?a+mod:a;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline int qpow(int a,int b)
{int ret=1;for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;}
inline int _inv(int x){return qpow(x,mod-2);}
/* math */
int n,k,x,y,p;
const int N=1e3+6;
int f[N][N];
typedef vector<int> poly;
poly F,A,R;
poly mul(poly a,poly b){
poly ret(a.size()+b.size()-1,0);
for(size_t i=0;i<a.size();i++)for(size_t j=0;j<b.size();j++)ret[i+j]=add(ret[i+j],mul(a[i],b[j]));
return ret;
}
poly Div(poly a,poly b){
int t=b[b.size()-1];
t=_inv(t);
for(size_t i=a.size()-1;i>=b.size()-1;i--){
int temp=mul(a[i],t);
for(size_t pos=i,j=b.size()-1;~j;pos--,j--){
a[pos]=sub(a[pos],mul(temp,b[j]));
}
}
a.resize(min(a.size(),b.size()-1));
return a;
}
inline int solve(int k){
memset(f,0,sizeof(f));
for(int j=0;j<=k+2;j++)f[0][j]=1;
for(int i=1;i<=k;i++)for(int j=k/i;~j;j--){
f[i][j]=mul(qpow(p,i),f[i][j+1]);
for(int k=1,P=sub(1,p);k<=i;k++,P=mul(P,p)){
f[i][j]=add(mul(f[k-1][j+1],mul(P,f[i-k][j])),f[i][j]);
}
}
F.resize(k+2);
for(int i=0;i<=k;i++)F[i]=mul(mul(sub(p,1),qpow(p,k-i)),f[k-i][1]);
F[k+1]=1;
A=poly(2,0);A[1]=1;
R=poly(1,1);
int K=n;
for(;K;K>>=1){
if(K&1){
R=mul(R,A);
R=Div(R,F);
}
A=mul(A,A);
A=Div(A,F);
}
int ret=0;
for(size_t i=0;i<R.size();i++){
ret=add(ret,mul(R[i],f[i][0]));
}
return ret;
}
int main()
{
cin >> n >> k >> x >> y;p=mul(x,_inv(y));
printf("%d\n",sub(solve(k),solve(k-1)));
}