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

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)=j=1kh(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;
}
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页