Codeforces 708E - Student's Camp(前缀和优化 dp)
神仙 *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}\):
\]
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)的更多相关文章
- LOJ 6089 小Y的背包计数问题 —— 前缀和优化DP
题目:https://loj.ac/problem/6089 对于 i <= √n ,设 f[i][j] 表示前 i 种,体积为 j 的方案数,那么 f[i][j] = ∑(1 <= k ...
- Codeforces 1132C - Painting the Fence - [前缀和优化]
题目链接:https://codeforces.com/contest/1132/problem/C 题意: 栅栏有 $n$ 个节,有 $q$ 个人可以雇佣来涂栅栏,第 $i$ 个人可以涂第 $l_i ...
- P5241 序列(滚动数组+前缀和优化dp)
P5241 序列 挺神仙的一题 看看除了dp好像没什么其他办法了 想着怎么构个具体的图出来,然鹅不太现实. 于是我们想办法用几个参数来表示dp数组 加了几条边肯定要的吧,于是加个参数$i$表示已加了$ ...
- CDOJ 1307 ABCDE 前缀和优化dp
ABCDE 题目连接: http://acm.uestc.edu.cn/#/problem/show/1307 Description Binary-coded decimal (BCD) is a ...
- bzoj 1044 [HAOI2008]木棍分割——前缀和优化dp
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1044 前缀和优化. 但开成long long会T.(仔细一看不用开long long) #i ...
- bzoj 3398 [Usaco2009 Feb]Bullcow 牡牛和牝牛——前缀和优化dp / 排列组合
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3398 好简单呀.而且是自己想出来的. dp[ i ]表示最后一个牡牛在 i 的方案数. 当前 ...
- bzoj2431: [HAOI2009]逆序对数列(前缀和优化dp)
2431: [HAOI2009]逆序对数列 Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 2312 Solved: 1330[Submit][Stat ...
- CF601C Kleofáš and the n-thlon(期望+前缀和优化dp)
传送门 解题思路 要求这个人的排名,我们可以先求出某个人比他排名靠前的概率,然后再乘上\(m-1\)即为答案.求某个人比他排名靠前可以用\(dp\),设\(f[i][j]\)表示前\(i\)场比赛某人 ...
- 5.19 省选模拟赛 小B的夏令营 概率 dp 前缀和优化dp
LINK:小B的夏令营 这道题是以前从没见过的优化dp的方法 不过也在情理之中. 注意读题 千万不要像我这个sb一样 考完连题意都不知道是啥. 一个长方形 要求从上到下联通的概率. 容易发现 K天只是 ...
随机推荐
- 使用固件库点亮led灯
1. 项目 使用STM32F103VE的固件库实现流水灯设计. 2. 代码 由于这是基于野火的视频进行学习的,项目代码在上节基础上进行编写的. 点亮绿灯: main.c #include " ...
- MySQL:提高笔记-1
MySQL:提高笔记-1 学完基础的语法后,进一步对 MySQL 进行学习 说明:这是根据 bilibili 上 黑马程序员 的课程 mysql入门到精通 后做的笔记 1. 索引 1.1 索引概述 M ...
- [Beta]the Agiles Scrum Meeting 11
会议时间:2020.5.26 21:00 1.每个人的工作 今天已完成的工作 成员 已完成的工作 issue yjy 帮助解决技术问题 tq 完成评测机新增评测指标 评测部分增加更多评测指标 wjx ...
- 软件案例分析——VS和VS Code
软件案例分析--VS和VS Code 项目 内容 这个作业属于哪个课程/ 2020年春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里? 软件案例分析 我在这个课程的目标是? 提高代码水平,熟 ...
- Vue报错 type check failed for prop “xxx“. Expected String with value “xx“,got Number with value ‘xx‘
vue报错 [Vue warn]: Invalid prop: type check failed for prop "name". Expected String with ...
- spring、spring boot中配置多数据源
在项目开发的过程中,有时我们有这样的需求,需要去调用别的系统中的数据,那么这个时候系统中就存在多个数据源了,那么我们如何来解决程序在运行的过程中到底是使用的那个数据源呢? 假设我们系统中存在2个数据源 ...
- GitHub Universe 2021|MS Reactor 邀你共聚年度盛会
GitHub Universe 2021 将于2021年10月27-28日(PDT)在线直播,MS Reactor 将与 CSDN 合作进行转播,与你一同观看这场全球开发者盛会. 关于 GitHub ...
- 请问为什么要用三极管驱动mos,直接用mos有什么缺点呢?
可能无法完全导通,电流可能过小使导通所需时间变长,最终导致发热严重 回复 举报 csaaa DIY七级 3# 发表于 2016-7-12 14:11:59 直接驱动mos也没什么问 ...
- sql 多表联合查询更新
sqlserver: update A a set a.i = b.k from B b where a.key = b.key oracle : update A a set a.i = (sele ...
- 在Delphi中高效执行JS代码
因为一些原因,需要进行encodeURIComponent和decodeURIComponent编码,在Delphi中找了一个,首先是发现不能正确编码+号,后面强制处理替换了,勉强可用. 后面发现多次 ...