Codeforces 题目传送门 & 洛谷题目传送门

神仙 *3100,%%%

首先容易注意到 \(\forall i\in[1,m]\),第 \(i\) 行剩余的砖块一定构成一个区间,设其为 \([l_i,r_i]\)。

其次,由于第 \(0\) 行和第 \(m+1\) 行的砖块不可能被风吹走,因此该建筑物只可能被上下劈开,i.e.,该建筑物被劈开当且仅当 \(\exist i\in[1,m),[l_i,r_i]\cap[l_{i+1},r_{i+1}]=\varnothing\)。

这时候就可以考虑 \(dp\) 了,\(dp_{i,l,r}\) 表示考虑到第 \(i\) 行,第 \(i\) 行剩余的砖块组成的区间为 \([l,r]\) 的概率。因此我们就有了优秀的 \(n^5\) 的 \(dp\):\(dp_{i,l,r}=P(l,r)\times\sum\limits_{[l,r]\cap[l',r']\ne \varnothing}dp_{i-1,l',r'}\),其中 \(P(l,r)\) 为某一行被风吹得恰好只剩 \([l,r]\) 的概率,如果我们记 \(f(i)\) 表示恰好 \(i\) 个砖块被吹走的概率,那么有 \(f(i)=\dbinom{k}{i}p^i(1-p)^{k-i}\),\(P(l,r)=f(l-1)f(m-r)\)。

显然我们要对这个 \(dp\) 进行优化。怎么优化呢?考虑问题的反面,可拿概率减去 \([l,r]\cap[l',r']=\varnothing\) 的概率,显然概率为 \(\sum\limits_{1\le l\le r\le m}dp_{i-1,l,r}\),而 \([l,r]\cap[l',r']=\varnothing\) 当且仅当 \(r'<l\lor l'>r\),故 \([l,r]\cap[l',r']=\varnothing\) 的概率为 \(\sum\limits_{1\le l'\le r'<l}dp_{i-1,l,r}+\sum\limits_{r<l'\le r'\le m}dp_{i-1,l,r}\),因此 \(dp_{i,l,r}=P(l,r)(\sum\limits_{1\le l\le r\le m}dp_{i-1,l,r}-\sum\limits_{1\le l'\le r'<l}dp_{i-1,l,r}-\sum\limits_{r<l'\le r'\le m}dp_{i-1,l,r})\),如果我们记 \(R_{i,r}\) 为右端点为 \(r\) 的 \(dp_{i,l,r}\) 的和,\(L_{i,l}\) 为左端点为 \(l\) 的 \(dp_{i,l,r}\) 的和,那么我们可预处理 \(R_{i,r}\) 的前缀和与 \(L_{i,l}\) 的后缀和,这样可实现 \(\mathcal O(1)\) 转移,复杂度降到了 \(n^3\)。

但这样还是会炸,事实上,我们状态数已经达到了 \(\mathcal O(n^3)\),光是数组都远远开不下,因此考虑优化状态。我们考虑不计算 \(dp_{i,l,r}\),直接计算 \(R_{i,r},L_{i,l}\) 并写出它们的转移方程式。我们不妨进一步改写下 \(dp_{i,l,r}\) 的转移方程式,将 \(\sum\) 展开成关于 \(L_{i,l},R_{i,r}\) 的表达式,那么有 \(dp_{i,l,r}=f(l-1)f(m-r)(\sum\limits_{j=1}^mR_{i-1,j}-\sum\limits_{j=1}^{l-1}R_{i-1,j}-\sum\limits_{j=r+1}^mL_{i-1,j})\)。还需注意到的一点是,容易发现 \(L_{i,l}\) 的转移方程式与 \(R_{i,r}\) 高度相似,事实上如果我们把每一排翻转过来的话即可发现 \(R_{i,r}\) 变成了 \(L_{i,l}\),因此我们可以得到 \(L_{i,l}=R_{i,m-l+1}\),故上述方程可进一步改写为 \(dp_{i,l,r}=f(l-1)f(m-r)(\sum\limits_{j=1}^mR_{i-1,j}-\sum\limits_{j=1}^{l-1}R_{i-1,j}-\sum\limits_{j=1}^{m-r}R_{i-1,j})\),我们考虑直接将 \(R_{i,r},dp_{i,l,r}\) 的方程式合并,即不经过 \(dp_{i,l,r}\),直接根据 \(R_{i-1,j}\) 的值转移到 \(R_{i,j}\)

\[\begin{aligned}R_{i,r}&=\sum\limits_{l=1}^rdp_{i,l,r}\\&=\sum\limits_{l=1}^rf(l-1)f(m-r)(\sum\limits_{j=1}^mR_{i-1,j}-\sum\limits_{j=1}^{l-1}R_{i-1,j}-\sum\limits_{j=1}^{m-r}R_{i-1,j})\\&=f(m-r)((\sum\limits_{l=1}^rf(l-1)(\sum\limits_{j=1}^mR_{i-1,j}-\sum\limits_{j=1}^{m-r}R_{i-1,j}))-\sum\limits_{l=1}^r\sum\limits_{j=1}^{l-1}f(l-1)R_{i-1,j}\end{aligned}
\]

emmm……推到这一步应该就非常好维护了吧。

记 \(SR_{i,j}=\sum\limits_{r=1}^jR_{i,r}\),即 \(R_{i,j}\) 的前缀和,那么 \(R_{i,r}=f(m-r)((\sum\limits_{l=1}^rf(l-1)(SR_{i-1,m}-SR_{i-1,m-r}))-\sum\limits_{l=1}^rf(l-1)SR_{i-1,l-1}\)

显然 \(SR_{i-1,m}-SR_{i-1,m-r}\) 是常数,可 \(\mathcal O(1)\) 求出,因此我们只需处理 \(f(j)\) 的前缀和和 \(f(j)SR_{i-1,j}\) 的前缀和即可实现 \(\mathcal O(1)\) 转移。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1.5e3;
const int MAXK=1e5;
const int MOD=1e9+7;
int n,m,a,b,k,p,fac[MAXK+5],ifac[MAXK+5];
void init_fac(int n){
fac[0]=ifac[0]=ifac[1]=1;
for(int i=2;i<=n;i++) ifac[i]=1ll*ifac[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD,ifac[i]=1ll*ifac[i]*ifac[i-1]%MOD;
}
int d[MAXN+5],sd[MAXN+5],ss[MAXN+5];
int dp[MAXN+5][MAXN+5],sdp[MAXN+5][MAXN+5];
int binom(int x,int y){return 1ll*fac[x]*ifac[x-y]%MOD*ifac[y]%MOD;}
int qpow(int x,int e=MOD-2){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int main(){
scanf("%d%d%d%d%d",&n,&m,&a,&b,&k);
p=1ll*a*qpow(b)%MOD;init_fac(k);
if(p==1){
if(k<=m) d[k]=1;
}
else{
int ivp=1ll*p*qpow(MOD+1-p)%MOD,pw=1;
for(int i=1;i<=k;i++) pw=1ll*pw*(MOD+1-p)%MOD;
for(int i=0;i<=m;i++) d[i]=1ll*binom(k,i)*pw%MOD,pw=1ll*pw*ivp%MOD;
}
sd[0]=d[0];for(int i=1;i<=m;i++) sd[i]=(sd[i-1]+d[i])%MOD;
dp[0][m]=1;sdp[0][m]=1;
for(int i=1;i<=n;i++){
memset(ss,0,sizeof(ss));
for(int j=1;j<=m;j++) ss[j]=(ss[j-1]+1ll*d[j]*sdp[i-1][j])%MOD;
for(int j=1;j<=m;j++){
dp[i][j]=1ll*d[m-j]*(1ll*(sdp[i-1][m]-sdp[i-1][m-j]+MOD)*sd[j-1]%MOD-ss[j-1]+MOD)%MOD;
// printf("%d %d %d\n",i,j,dp[i][j]);
}
for(int j=1;j<=m;j++) sdp[i][j]=(sdp[i][j-1]+dp[i][j])%MOD;
} printf("%d\n",sdp[n][m]);
return 0;
}

Codeforces 708E - Student's Camp(前缀和优化 dp)的更多相关文章

  1. LOJ 6089 小Y的背包计数问题 —— 前缀和优化DP

    题目:https://loj.ac/problem/6089 对于 i <= √n ,设 f[i][j] 表示前 i 种,体积为 j 的方案数,那么 f[i][j] = ∑(1 <= k ...

  2. Codeforces 1132C - Painting the Fence - [前缀和优化]

    题目链接:https://codeforces.com/contest/1132/problem/C 题意: 栅栏有 $n$ 个节,有 $q$ 个人可以雇佣来涂栅栏,第 $i$ 个人可以涂第 $l_i ...

  3. P5241 序列(滚动数组+前缀和优化dp)

    P5241 序列 挺神仙的一题 看看除了dp好像没什么其他办法了 想着怎么构个具体的图出来,然鹅不太现实. 于是我们想办法用几个参数来表示dp数组 加了几条边肯定要的吧,于是加个参数$i$表示已加了$ ...

  4. CDOJ 1307 ABCDE 前缀和优化dp

    ABCDE 题目连接: http://acm.uestc.edu.cn/#/problem/show/1307 Description Binary-coded decimal (BCD) is a ...

  5. bzoj 1044 [HAOI2008]木棍分割——前缀和优化dp

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1044 前缀和优化. 但开成long long会T.(仔细一看不用开long long) #i ...

  6. bzoj 3398 [Usaco2009 Feb]Bullcow 牡牛和牝牛——前缀和优化dp / 排列组合

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3398 好简单呀.而且是自己想出来的. dp[ i ]表示最后一个牡牛在 i 的方案数. 当前 ...

  7. bzoj2431: [HAOI2009]逆序对数列(前缀和优化dp)

    2431: [HAOI2009]逆序对数列 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 2312  Solved: 1330[Submit][Stat ...

  8. CF601C Kleofáš and the n-thlon(期望+前缀和优化dp)

    传送门 解题思路 要求这个人的排名,我们可以先求出某个人比他排名靠前的概率,然后再乘上\(m-1\)即为答案.求某个人比他排名靠前可以用\(dp\),设\(f[i][j]\)表示前\(i\)场比赛某人 ...

  9. 5.19 省选模拟赛 小B的夏令营 概率 dp 前缀和优化dp

    LINK:小B的夏令营 这道题是以前从没见过的优化dp的方法 不过也在情理之中. 注意读题 千万不要像我这个sb一样 考完连题意都不知道是啥. 一个长方形 要求从上到下联通的概率. 容易发现 K天只是 ...

随机推荐

  1. Java多线程中的死锁

    Java多线程中的死锁 死锁产生的原因 线程死锁是指由两个以上的线程互相持有对方所需要的资源,导致线程处于等待状态,无法往前执行. 当线程进入对象的synchronized代码块时,便占有了资源,直到 ...

  2. Github Actions 实践

    Github Actions 实践 Github Actions 是 Github 的持续集成服务,通过在 repo 发生特定的行为时执行指定的命令实现自动测试.自动部署等功能. 基本术语 workf ...

  3. [技术博客]WEB实现划词右键操作

    [技术博客]WEB实现划词右键操作 一.功能解释 简单地对题目中描述的功能进行解释:在浏览器中,通过拖动鼠标选中一个词(或一段文字),右键弹出菜单,且菜单为自定义菜单,而非浏览器本身的菜单.类似的功能 ...

  4. NOIP模拟84(多校17)

    T1 宝藏 解题思路 考场上一眼出 \(nlog^2\) 做法,然后没看见是 1s 3e5 的数据,我竟然以为自己切了?? 考完之后尝试着把二分改为指针的移动,然后就过了??或许是数据水吧,感觉自己的 ...

  5. CSP踩被记

    本来想起个清新脱俗的标题,但碍于语文功底不行,于是光明正大嫖了LiBoyi的高端创意,把这篇博客命名为踩被记. Day -6 用假暴力把真正解拍没了,伤心.Rp有点低 Day -4 信息学考,\(py ...

  6. hdu 1394 Minimum Inversion Number(线段树or树状数组)

    题意: 给你N个数,N个数是0~N-1的一个全排列. 要求统计它的所有形式的逆序对的最小值.它的所有形式的意思是,不断将数组开头的第一个数放到数组的最后面. 逆序对:i<j且ai>aj 思 ...

  7. oeasy教您玩转vim - 56 - # 字符可视化模式

    ​ 可视化编辑 回忆上节课内容 我们学习了关于模式匹配中使用参数 单个参数 :%s/<h2>\(.*\)</h2>/ - \1/g 多个参数 :%s/<img src=\ ...

  8. dns+nginx实现多虚拟主机

    借鉴于朋友的需求,公司需要启用域名访问内部的业务系统,现实情况是内部的业务系统目前使用的是单主机,单nginx多端口的方式再运行,朋友最终想实现启用域名方式问题,且域名不需要用户手工输入端口号 两种思 ...

  9. nohup java -jar xx.jar & ,关闭窗口后退出进程

    nohup java -jar dw-report..jar > dw-report.log  & 自动退出命令在后台运行 xx.jar程序 明明已经加了"&" ...

  10. 【Go语言学习笔记】hello world

    书接上回,上回说到了为什么要学习Go语言,今天我们来实际写一下,感受一下Go语言的精美之处. 环境搭建 安装和设置 Windows: Go安装包下载网址:https://golang.org/dl/ ...