• [NOI2017]泳池


    XXVIII.[NOI2017]泳池

    常系数齐次线性递推的应用。

    我们首先将问题转换为(面积小于等于\(K\)的方案数)减去(面积小于等于\(K-1\)的方案数)。

    然后考虑两个东西分别DP。我们设当前考虑的是面积小于等于\(m\)的情况。

    我们设\(f_{i,j}\)表示考虑一段长为\(i\)的沙滩,其中前\(j-1\)行都是安全的,而第\(j\)行至少有一个危险的地方时,只存在面积小于等于\(m\)的游泳池的概率。再设\(p\)为一个格子是安全的概率,\(q\)则为它是危险的概率。

    注意,这里我们不考虑前\(j-1\)行都是安全的概率——即,最终要乘上一个\(p^{i(j-1)}\)才是正确概率。

    我们考虑再设\(g_{i,j}\)表示前\(j-1\)行都是安全的,第\(j\)行往后可能出现危险格子,此时不存在面积大于\(m\)的游泳池的方案数。

    则我们应有

    \[g_{i,j}=\sum\limits_{k\geq j}f_{i,j}\times p^{i(k-j)} \]

    后面那个\(p^{i(k-j)}\)是第\(j\)到第\(k-1\)行全部安全的方案数。

    它可以通过后缀和的形式求出:

    \[g_{i,j}=g_{i,j+1}\times p^i+f_{i,j} \]

    下面我们考虑求出\(f\)。我们考虑枚举第一个危险格子出现在哪里。则我们就有

    \[f_{i,j}=\sum\limits_{k=1}^i\Big(\sum\limits_{l\geq k+1}f_{k-1,l}\times p^{i(l-j)}\Big)\times q\times\Big(\sum\limits_{l\geq k}f_{i-k,l}\times p^{i(l-j)}\Big) \]

    因为前一半中,前\(j\)行内必须没有任何东西;而后一半中,前\(j-1\)行必须没有任何东西;中间的一个位置还必须是危险的。所以我们可以得到上面的转移式。

    \(g\)来替代,就得到了

    \[f_{i,j}=\sum\limits_{k=1}^i(g_{k-1,j+1}\times p^i)\times q\times g_{i-k,j} \]

    因为\(f_{i,j}\)合法的前提,是\(i(j-1)\leq m\),故实际上只有\(m\log m\)\(f_{i,j}\)是合法的;对于每个\(f_{i,j}\),都\(O(m)\)地转移,就可以在\(O(m^2\log m)\)时间内完成。

    (实际上,观察到转移是个卷积,也可以用NTT优化到\(O(m\log^2n)\),但是没有必要)

    我们考虑设\(h_i\)表示一个长度为\(i\)的海滩不存在大于\(m\)的游泳池的方案数。

    我们再设\(a_i\)表示一段长度为\(i\)且第\(1\)列内仅有最左端有一个危险格子的海滩的概率。则有\(a_i=g_{i-1,2}\times p^{i-1}\)

    则我们有

    \[h_i=\sum\limits_{j=1}^{m+1}h_{i-j}a_j\qquad(i>m) \]

    这是常系数齐次线性递推的形式!!!

    先别急,我们再来考虑一下\(i\leq m\)这一段的初始值:

    \[h_i=a_{i+1}+\sum\limits_{j=1}^{i}h_{i-j}a_j\qquad(i\leq m) \]

    然后就是模板了。这里多项式乘法和多项式取模都可选择直接暴力\(O(m^2)\)处理,因为\(m\)只有\(1000\)

    #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;
    } 
    

  • 相关阅读:
    JAVA_集合_作业01
    Java_Objects_hashCode
    Java成员内部类
    Java集合练习_实现购物车需求
    Java初级_单例设计模式
    HelloWorld
    vue中视频标点
    防抖节流?俩者区别?vue如何使用防抖、节流来解决点击问题?
    keep-alive
    axios和ajax的区别
  • 原文地址:https://www.cnblogs.com/Troverld/p/14608077.html
Copyright © 2020-2023  润新知