[概率DP][多项式取模] NOI2017.day1 T3 泳池

来源:互联网 发布:webclient post json 编辑:程序博客网 时间:2024/04/30 18:00

lzz写了题解,但是我没看懂…
就自己想了个DP
f(i,j)表示高度为i,长度为j的局域,i这个行存在障碍,前i-1行不存在障碍,能选取的区域<=k的概率

那么fi,j=k<jfi,kpi1qgi,jk1+k<jgi,kgi,jk1pi1q
其中p为一个格子安全的概率,q,为一个格子危险的概率。
gi,j表示i行,长度为j的区域不存在障碍,能选去的区域<=k的概率
那么gi,j=s>ifs,k

这样转移一下就可以了
可以按段转移,设h(i)为长度为i的答案
那么h(i)=kj=1h(ij)g(1,j)
这是一个线性递推式,用多项式取模可以完成。

#include <cstdio>#include <iostream>#include <algorithm>#include <string>#include <cstring>using namespace std;const int N=5100,P=998244353;inline int Pow(int x,int y){  int ret=1;  for(;y;y>>=1,x=1LL*x*x%P) if(y&1) ret=1LL*ret*x%P;  return ret;}inline int inv(int x){  return Pow(x,P-2);}inline void add(int &x,int y){  (x+=y)%=P;}int n,k,x,y,p,q;int f[N][N],g[N],pw[N],h[N],a[N],F[N],d[N];int rev[N],w[2][N],num;inline void NTT(int *a,int n,int r){  for(int i=1;i<n;i++) if(rev[i]>i) swap(a[i],a[rev[i]]);  for(int i=1;i<n;i<<=1)    for(int j=0;j<n;j+=i<<1)      for(int k=0;k<i;k++){    int x=a[j+k],y=1LL*w[r][n/(i<<1)*k]*a[i+j+k]%P;    a[j+k]=(x+y)%P; a[i+j+k]=(x+P-y)%P;      }  if(!r) for(int i=0,iv=Pow(n,P-2);i<n;i++) a[i]=1LL*a[i]*iv%P;}inline void Pre(int n,int l){  for(int i=1;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<l);  int g=Pow(3,(P-1)/n),ig=inv(g); w[0][0]=w[1][0]=1;  for(int i=1;i<n;i++)    w[0][i]=1LL*w[0][i-1]*ig%P,w[1][i]=1LL*w[1][i-1]*g%P;}inline void Mul(int *a,int *b,int m){  int l=-1,M; for(M=1;M<(m<<1);M<<=1) l++; Pre(M,l);  if(a==b){    NTT(a,M,1);    for(int i=0;i<M;i++) a[i]=1LL*a[i]*a[i]%P;    NTT(a,M,0);    return ;  }  NTT(a,M,1); NTT(b,M,1);  for(int i=0;i<M;i++) a[i]=1LL*a[i]*b[i]%P;  NTT(a,M,0); NTT(b,M,0);}int tmp[N],A[N],B[N];void Inv(int *a,int *b,int n){  if(n==1) return b[0]=inv(a[0]),void();  Inv(a,b,n+1>>1);  static int tmp[N];  int L=0; while(!(n>>L&1)) L++; Pre(n<<1,L);  for(int i=0;i<n;i++) tmp[i]=a[i];  for(int i=n;i<n<<1;i++) tmp[i]=0;  NTT(tmp,n<<1,1); NTT(b,n<<1,1);  for(int i=0;i<n<<1;i++)    tmp[i]=(2LL*b[i]%P+P-1LL*tmp[i]*b[i]%P*b[i]%P)%P;  NTT(tmp,n<<1,0);  for(int i=0;i<n;i++) b[i]=tmp[i];  for(int i=n;i<n<<1;i++) b[i]=0;}int t[N],bb[N];inline void Div(int *a,int n,int *b,int m){  static int tmp[N],A[N],B[N];  for(int i=0;i<m;i++) t[i]=b[m-i-1];  for(int i=0;i<n;i++) A[i]=a[n-i-1];  int nn=1,d=n-m+1; for(;nn<d<<1;nn<<=1);  for(int i=n;i<nn;i++) A[i]=0;  for(int i=d;i<nn;i++) t[i]=0;  for(int i=0;i<nn;i++) B[i]=0;  Inv(t,B,nn);  for(int i=d;i<nn;i++) B[i]=0;  Mul(A,B,max(n,d));  for(int i=d;i<=n<<1;i++) A[i]=0;  for(int i=0;i<d;i++) if(i>d-i-1) swap(A[i],A[d-i-1]);  for(int i=0;i<m;i++) t[i]=b[i];  Mul(t,A,max(d,m));  for(int i=0;i<n;i++) a[i]=(a[i]+P-t[i])%P;}inline void mul(int *a,int *b,int m){  Mul(a,b,m);  Div(a,2*m,d,m+1);}inline void Pow(int *a,int m,int n,int *b){  for(;n;n>>=1,mul(a,a,m))    if(n&1) mul(b,a,m);}inline int solve(int m){  memset(f,0,sizeof(f)); memset(g,0,sizeof(g));  f[m+1][1]=1LL*pw[m]*q%P; g[0]=1; g[1]=f[m+1][1];  for(int i=m;i;i--){    f[i][0]=1;    for(int j=1;(i-1)*j<=m && j<=m;j++)      for(int s=1;s<=j;s++){                if(s>1) add(f[i][j],1LL*f[i][s-1]*g[j-s]%P*pw[i-1]%P*q%P);                add(f[i][j],1LL*g[s-1]*g[j-s]%P*pw[i-1]%P*q%P);      }    if(i==1) memcpy(h,g,sizeof(h));    for(int j=1;(i-1)*j<=m && j<=m;j++)      add(g[j],f[i][j]);  }  if(n<=m) return g[n];  for(int i=m+1;i;i--)    h[i]=1LL*h[i-1]*q%P;  m++;  memset(a,0,sizeof(a)); memset(d,0,sizeof(d));  memset(F,0,sizeof(F));  for(int i=0;i<m;i++) d[i]=(P-h[m-i])%P;  d[m]=1; a[1]=1;  F[0]=1;  Pow(a,m,n,F);  int ret=0;  for(int i=0;i<m;i++) add(ret,1LL*F[i]*g[i]%P);  return ret;}int main(){  scanf("%d%d%d%d",&n,&k,&x,&y);  p=1LL*x*inv(y)%P; q=(1+P-p)%P; pw[0]=1;  for(int i=1;i<=k;i++) pw[i]=1LL*pw[i-1]*p%P;  printf("%d\n",(solve(k)+P-solve(k-1))%P);  return 0;}