大意: $n$行$m$列砖, 白天左侧边界每块砖有$p$概率被摧毁, 晚上右侧边界有$p$概率被摧毁, 求最后上下边界连通的概率.

记${dp}_{i,l,r}$为遍历到第$t$行时, 第$t$行砖块范围$[l,r]$的概率.

有${dp}_{i,l,r}=p_{l,r}\sum {dp}_{i-1,l',r'}$ (要满足$[l',r']$与$[l,r]$相交)

$p_{l,r}$表示$k$天后剩余砖是$[l,r]$的概率.

考虑二维前缀优化, 记$f_{i,l,r}=\sum\limits_{1\le x\le l}\sum\limits_{1\le y\le r} {dp}_{i,x,y}$, 就有

$$\begin{align}  {dp}_{i,l,r} &=p_{l,r}(f_{i-1,m,r}+f_{i-1,r,m}-f_{i-1,m,l-1}-f_{i-1,r,r}) \notag\\ &=p_{l,r}(f_{i-1,r,m}-f_{i-1,l-1,l-1}) \notag\end{align}$$

最后所求答案为$f_{n,m,m}$. 但是这样复杂度是$O(nm^2)$

可以注意到$dp$的式子中所需要的$f$值非常少.

记$A_{i,x}=f_{i,x,m},B_{i,x}=f_{i,x,x}$, 有

$$dp_{i,l,r}=p_{l,r}(A_{i-1,r}-B_{i-1,l-1})$$

$$A_{i,x}=A_{i,x-1}+\sum\limits_{x\le k\le m}{dp}_{i,x,k}$$

$$B_{i,x}=B_{i,x-1}+\sum\limits_{1\le k\le x}{dp}_{i,k,x}$$

然后再进行前缀优化, 复杂度即为$O(nm)$

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <math.h>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <string.h>
#include <bitset>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head const int N = 1e6+10, M = 2e3+10;
int n, m, a, b, k;
int fac[N],ifac[N],p[M],suf[M],pre[M];
int A[2][M],B[2][M],s1[M],s2[M]; void init() {
fac[0]=1;
REP(i,1,N-1) fac[i]=fac[i-1]*(ll)i%P;
ifac[N-1]=inv(fac[N-1]);
PER(i,0,N-2) ifac[i]=ifac[i+1]*(i+1ll)%P;
}
int C(int n, int m) {
if (n<m) return 0;
return (ll)fac[n]*ifac[m]%P*ifac[n-m]%P;
} int main() {
init();
cin>>n>>m>>a>>b>>k;
int x = (ll)a*inv(b)%P, y = (ll)(P+b-a)*inv(b)%P;
REP(i,1,min(m,k+1)) p[i] = (ll)C(k,i-1)*qpow(x,i-1)%P*qpow(y,k-i+1)%P;
REP(i,1,m) {
suf[i] = (suf[i-1]+p[m-i+1])%P;
pre[i] = (pre[i-1]+p[i])%P;
}
REP(i,1,m) A[0][i] = 1;
int cur = 0;
REP(i,1,n) {
cur ^= 1;
REP(k,1,m) s1[k]=(s1[k-1]+(ll)p[m-k+1]*A[!cur][k])%P;
REP(k,1,m) s2[k]=(s2[k-1]-(ll)p[k]*B[!cur][k-1])%P;
REP(x,1,m) {
A[cur][x] = A[cur][x-1];
int ret = (s1[m]-s1[x-1])%P;
ret = (ret-(ll)(suf[m]-suf[x-1])*B[!cur][x-1])%P;
A[cur][x] = (A[cur][x]+(ll)p[x]*ret)%P;
}
REP(x,1,m) {
B[cur][x] = B[cur][x-1];
int ret = (s2[x]+(ll)pre[x]*A[!cur][x])%P;
B[cur][x] = (B[cur][x]+(ll)p[m-x+1]*ret)%P;
}
}
int ans = B[cur][m];
if (ans<0) ans += P;
printf("%d\n", ans);
}

最新文章

  1. mysql定义和调用存储过程
  2. CLR via C# 学习计划
  3. Visual studio智能感知挡住了当前代码输入行
  4. 【noiOJ】P1996
  5. [转]Jquery通用开源框架之【ejq.js】
  6. ios开发中,A valid provisioning profile for this executable was not found,的解决方法
  7. SQL的定义与使用
  8. java中gson的简单使用
  9. HTML5中video的使用一
  10. 使用NeatUpload控件实现ASP.NET大文件上传
  11. Android学习笔记之Broadcast Receiver
  12. vim编辑器介绍及其常用命令
  13. CENTOS6.6下mysql MMM架构搭建
  14. gym 101755
  15. PHP提交表单失败后如何保留填写的信息
  16. git如何撤销git add操作?
  17. [转]Windows下使用VS2015编译openssl库
  18. Android Studio 常见问题汇总
  19. 打开CDQ的大门&amp;BZOJ3262
  20. blkblock 2工具

热门文章

  1. Java IO系统--RandomAccessFile
  2. 深拷贝(deep clone)与浅拷贝(shallow clone)
  3. python中list和dict
  4. mvn创建flink项目
  5. Java基础 main 参数String[] args的用法
  6. 创建WebApi Odata v3 终结点
  7. linux(centos7.0以上版本)安装 mysql-5.7.24-linux-glibc2.12-x86_64.tar 版本的mysql
  8. Sword cjson库函数使用
  9. bootstrap 输入框只能数字和字母等其他限制
  10. python哲学