@codeforces - 618G@ Combining Slimes
@description@
一行上摆有 n 个方格。每一次你可以在最右边的方格滴入一滴史莱姆。有 p 的概率该史莱姆大小为 1,有 (1 - p) 的概率该史莱姆大小为 2。
史莱姆会不断往左滚动,直到遇到另一个史莱姆或边界。假如遇到的是大小相同的史莱姆则合并,大小加一,继续往左滚动;否则直接停下。
等到无法操作时,问最后方格内的史莱姆大小总和的期望值。
input:
两个整数 n,p。(1 <= n <= 10^9,1 <= p < 10^9)
其中对应的概率为 p/10^9 与 1 - p/10^9。
output:
输出期望值。
sample input:
2 500000000
sample output:
3.562500000000000
sample explain:
滴入大小为 1 与 2 的史莱姆的概率都为 1/2。
1/4 的概率最终状态为 1 2,3/8 最终为 2 1,3/16 最终为 3 2 与 3/16 最终为 3 1。
期望值为 \(1/4*(1 + 2) + 3/8*(2 + 1) + 3/16*(3 + 2) + 3/16*(3 + 1) = 3.5625\)
@solution@
这道题有点儿意思。
@part - 0@
嘛……首先发现当一个大小为 1 的史莱姆在一个大小为 2 的史莱姆左边,则它们以及它们左边的史莱姆永远不会再变动的。
然后呢?然后我就不会做了。
@part - 1@
根据题解所写,我们要发现这样一个关键的性质:
假如我们需要弄出来大小为 s 的史莱姆,我们需要先弄出来两个大小为 (s-1) 的史莱姆。
令弄出大小为 s 的概率为 \(pr(s)\),则有递推公式 \(pr(s) = pr^2(s-1)\),边界 \(pr(2) = (1-p) + p^2\)。
这是一个理想的上界,实际概率可能还要小些。
但是就这种情况下,取 \(pr(2)_{max}=(1-\frac{1}{10^9})\)(不是准确值),则当 s >= 50 时, \(pr(s)\) 已经趋近于无穷小了(可以自行算),不会再对答案产生影响。
所以史莱姆的大小可以控制到 50 以下。
……
MMP 鬼才会去想这个性质。
@part - 2@
其实下面这个性质并不难想到,但是如果没有上面那个性质也运用不了。令 \(pr(s, n)\)表示 n 个方格弄出大小为 s 的史莱姆的概率。
可以发现,当 n 充分大时, \(pr(s, n) = pr(s, n+1)\)。
感性理解一下:你方格数量多了,反而有些方格可能用不到。将这些用不到的方格去掉,概率依然还是不变的。
这个充分大是多大呢?
\(pr(s, n)\) 之间有递推关系 \(pr(s, n) = pr(s-1, n)*pr(s-1, n-1)\)。
边界条件 \(\begin{cases}pr(1, 1) = p, pr(2, 1) = 1-p\\pr(1, n) = p, pr(2,n) = (1-p)+p^2&n\not = 1\end{cases}\)
我们可以发现边界条件其实跟 n 没有关系。然后递推关系中,每一次 s 都要减少 1,所以 s 过后就会碰到边界条件。
所以,当 n > s 时,总有 \(pr(s, n) = pr(s, n+1)\)。
因此只要我们已知 \(pr(s, n)(s \le 50, n \le 50)\) 的值,就可以表示所有的 \(pr(s, n)\) 的值。
然而还有个问题,倒回到我们一开始说的那个性质:有可能我们的最左边固定的是一个 1 2 的形式。
为了解决这个问题,我们令 \(pr'(s, n)\) 表示 n 个方格,最左边已经有一个大小为 2 的史莱姆的前提下,弄出大小为 s 的史莱姆的概率。
类似地可以得到递推关系:\(pr'(s, n)=pr'(s-1,n)*pr(s-1,n-1)\)。
边界条件不再复述,可以自行推导,也可以等会儿看代码。
@part - 3@
发现的这些性质……所以到底有啥子用?
既然上文中有这么多递推关系,我们就来想想用我们的 dp 来搞这道题。
令 \(dp(s, n)\) 表示最终局面从右往左数前 n 个格子,第 n 个格子的史莱姆大小为 s 的前提下,的期望大小和。根据上文, s <= 50。
令 \(f(s, n)=pr(s, n)*(1-pr(s, n-1))\),含义为第 n 个格子史莱姆大小为 s,第 n-1 个格子史莱姆大小 < s 的概率。这样第 n 个格子史莱姆绝对不会与第 n-1 个合并,只要第 n+1 个格子的史莱姆不与第 n 个格子合并,整个局面就是“稳定的”。
转移时,枚举第 i+1 个格子大小为 k,则:
\]
比较难理解的可能就是右边乘上来的概率为什么长成那样。
其实这(应该)是一个条件概率。
条件概率的公式为 \(P(B|A)=\dfrac{P(AB)}{P(A)}\),即在事件 A 发生的前提下,事件 B 发生的概率等于事件 A B 同时发生的概率除以 A 发生的概率。
对应到该题。事件 A 为 “最终局面中第 i 个格子的史莱姆大小为 j ”,事件 B 为 “第 i+1 个格子的史莱姆大小为 k”。
嗯。我相信它是这样的。
为什么不直接用 \(dp(s, n)\) 乘上一个 \(f(s, n)\) 呢?因为当 j = 1 时,上面那个转移式,就不能那么转移了。因为大小为 1 后面可能接个 2。
我们令 \(g(s, n)=pr'(s, n)*(1-pr(s, n-1))\)。则:
\]
然后,由于我们最后发现的那个性质,所以当 n 充分大时右边乘上来的概率是不会变化的,是个常数。
所以我们就可以跑矩阵加速了。
最后得到 \(ans = \sum_{i=1}^{50}f(i, n)*dp(i, n)\)。
【我才不会说题解基本全靠口胡和感性认知 QAQ】
【因为我自己也不是太懂这道题 QAQ】
@accepted code@
通过浮点数的精度,使得某项变量因为远远超过题目所给的精度而忽略它。
这种类型我还只在 NOI2016 的旷野大计算那道题见过,果然还是太弱 QAQ。
//代码极丑无比,请勿模仿。
#include<cstdio>
const int MAXN = 50;
double pr1[MAXN + 5][MAXN + 5], pr2[MAXN + 5][MAXN + 5];
double f[MAXN + 5][MAXN + 5], g[MAXN + 5][MAXN + 5];
double dp[MAXN + 5][MAXN + 5];
struct matrix{
double m[MAXN + 5][MAXN + 5];
int r, c;
}M, R;
void init(double p) {
pr1[1][1] = p, pr1[2][1] = 1-p;
f[1][1] = p, f[2][1] = 1-p, g[2][1] = 1;
for(int i=2;i<=MAXN;i++) {
pr1[1][i] = p, pr1[2][i] = (1-p) + p*p, pr2[2][i] = 1;
for(int j=3;j<=MAXN;j++)
pr1[j][i] = pr1[j-1][i]*pr1[j-1][i-1], pr2[j][i] = pr2[j-1][i]*pr1[j-1][i-1];
for(int j=1;j<=MAXN;j++)
f[j][i] = pr1[j][i]*(1-pr1[j][i-1]), g[j][i] = pr2[j][i]*(1-pr1[j][i-1]);
}
dp[1][1] = 1, dp[2][1] = 2;
for(int i=2;i<=MAXN;i++) {
double del = 0; dp[1][i] = 1;
for(int l=1;l<=50;l++)
del += g[l][i-1];
for(int k=1;k<=MAXN;k++)
dp[1][i] += dp[k][i-1]*g[k][i-1]/del;
del = 0;
for(int j=2;j<=MAXN;j++) {
dp[j][i] = j; del += f[j-1][i-1];
for(int k=1;k<j;k++)
dp[j][i] += dp[k][i-1]*f[k][i-1]/del;
}
}
M.r = M.c = MAXN+1;
double del = 0;
for(int i=1;i<=MAXN;i++)
del += g[i][MAXN];
for(int i=1;i<=MAXN;i++)
M.m[1][i] = g[i][MAXN]/del;
del = 0;
for(int i=2;i<=MAXN;i++) {
del += f[i-1][MAXN];
for(int j=1;j<i;j++)
M.m[i][j] = f[j][MAXN]/del;
for(int j=i;j<=MAXN;j++)
M.m[i][j] = 0;
}
for(int i=1;i<=MAXN;i++)
M.m[0][i] = 0, M.m[i][0] = i;
M.m[0][0] = 1;
R.r = MAXN+1, R.c = 1;
for(int i=1;i<=MAXN;i++)
R.m[i][0] = dp[i][MAXN];
R.m[0][0] = 1;
}
matrix operator * (matrix A, matrix B) {
matrix C; C.r = A.r, C.c = B.c;
for(int i=0;i<C.r;i++)
for(int j=0;j<C.c;j++)
C.m[i][j] = 0;
for(int i=0;i<A.r;i++)
for(int j=0;j<B.c;j++)
for(int k=0;k<A.c;k++)
C.m[i][j] += A.m[i][k] * B.m[k][j];
return C;
}
matrix quick_pow(matrix b, int p) {
matrix ret; ret.r = ret.c = b.r;
for(int i=0;i<ret.r;i++)
for(int j=0;j<ret.c;j++)
ret.m[i][j] = (i == j);
while( p ) {
if( p & 1 ) ret = ret * b;
b = b * b;
p >>= 1;
}
return ret;
}
int main() {
int n, p;
scanf("%d%d", &n, &p);
init(p/1E9);
if( n <= MAXN ) {
double ans = 0;
for(int i=1;i<=MAXN;i++)
ans += f[i][n]*dp[i][n];
printf("%lf\n", ans);
}
else {
R = quick_pow(M, n-MAXN)*R;
double ans = 0;
for(int i=1;i<=MAXN;i++)
ans += f[i][MAXN]*R.m[i][0];
printf("%lf\n", ans);
}
}
@details@
不知道为什么想这道题的时候脑子里有一堆史莱姆在滚来滚去。好可爱来着 p(# ̄▽ ̄#)o。
我看了好久才发现那个转移式是个条件概率 QAQ。可能有其他的理解方法,但是我太弱了真的想不到,只能用条件概率去解释 QAQ。
各位过路的 dalao 如果能提供更简洁的理解思路麻烦留言在下面好吗 QAQ。
救救蒟蒻吧 QAQ。
@codeforces - 618G@ Combining Slimes的更多相关文章
- 【CF618G】Combining Slimes 概率+矩阵乘法
[CF618G]Combining Slimes 题意:一个长度为$1\times n$的网格,每次从最右侧往里推入一个数字1或2(数字会一直跑到最左边的空格子里),加入1的概率为p,2的概率为1-p ...
- ZJOI2018游记Round1
广告 ZJOI2018Round2游记 All Falls Down 非常感谢学弟学妹们捧场游记虽然这是一篇假游记 ZJOI Round1今天正式落下帷幕.在这过去的三天里遇到了很多朋友,见识了很多有 ...
- CodeForces 618A Slime Combining
http://www.codeforces.com/contest/618/problem/A 明明觉得是水题,而我却做了一个小时. 明明觉得代码没有错,而我却错了好几次. 因为我的名字不叫明明,也不 ...
- Wunder Fund Round 2016 (Div. 1 + Div. 2 combined) A. Slime Combining 水题
A. Slime Combining 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=2768 Description Your frien ...
- 关于Cewu Lu等的《Combining Sketch and Tone for Pencil Drawing Production》一文铅笔画算法的理解和笔录。
相关论文的链接:Combining Sketch and Tone for Pencil Drawing Production 第一次看<Combining Sketch and Tone f ...
- python爬虫学习(5) —— 扒一下codeforces题面
上一次我们拿学校的URP做了个小小的demo.... 其实我们还可以把每个学生的证件照爬下来做成一个证件照校花校草评比 另外也可以写一个物理实验自动选课... 但是出于多种原因,,还是绕开这些敏感话题 ...
- 【Codeforces 738D】Sea Battle(贪心)
http://codeforces.com/contest/738/problem/D Galya is playing one-dimensional Sea Battle on a 1 × n g ...
- 【Codeforces 738C】Road to Cinema
http://codeforces.com/contest/738/problem/C Vasya is currently at a car rental service, and he wants ...
- 【Codeforces 738A】Interview with Oleg
http://codeforces.com/contest/738/problem/A Polycarp has interviewed Oleg and has written the interv ...
随机推荐
- WPF 动画执行后属性无法修改
在做了一个类似QQ展开的动画时,设置了TopProperty,通过改变Window.Top属性来实现展开特效, 但是动画执行了之后,再去设置Window.Top的时候发现修改不了,代码调试后发现值设置 ...
- 命令模式(Command、Recevier、Invoker)(电脑开机命令)
(将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能.) 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是 ...
- 洛谷P2468 [SDOI2010]粟粟的书架
来了来了,随便拽一道题写题解[大雾] 最近发现自己基础奇差于是开始复习之前学过的东西,正好主席树我几乎完全没学会,然后打开洛谷试炼场… 发现了这么一道二合一的题. 这道题其实分成两个部分,前50%是一 ...
- Java 8最快的垃圾收集器是什么?
OpenJDK 8 有多种 GC(Garbage Collector)算法,如 Parallel GC.CMS 和 G1.哪一个才是最快的呢?如果在 Java 9 中将 Java 8 默认的 GC 从 ...
- 洛谷P1164 小A点菜 [2017年4月计划 动态规划08]
P1164 小A点菜 题目背景 uim神犇拿到了uoi的ra(镭牌)后,立刻拉着基友小A到了一家……餐馆,很低端的那种. uim指着墙上的价目表(太低级了没有菜单),说:“随便点”. 题目描述 不过u ...
- 将自己的代码托管到github - 秦时明月 - CSDN博客
步骤: 1.建立自己的github 2.安装github客户端,并配置身份 3.建立github项目 4.将github项目库下载到本地 5.提交本地代码到github 详细操作: 1.github网 ...
- LUGOU P3907 圈的异或
传送门 解题思路 其实就是找出所有的环判断,因为数据范围很小直接暴力做,注意要判断自环. 代码 #include<iostream> #include<cstdio> #inc ...
- 订阅 如何在IntelliJ IDEA中使用.ignore插件忽略不必要提交的文件
如何在IntelliJ IDEA中使用.ignore插件忽略不必要提交的文件 标签: idea git 插件 分类: Git 最近初学Git,而且在使用的IDE是IntelliJ IDEA,发现IDE ...
- 开源中国 ThinkPHP 领奖
开源中国 ThinkPHP 的领奖 周日早上早早就起来参考开源中国的活动. 由于今年竞争激烈 FastAdmin 没有上榜,但是没关系,因为这说明整个开源环境越来越好了,对于我们来说是利好. 因为 T ...
- 【POJ 3261】Milk Patterns
[链接]h在这里写链接 [题意] 给你一个长度为n的序列. 问你能不能在其中找到一个最长的子串. 这个子串至少出现了k次. [题解] 长度越长,就越不可能出现k次 后缀数组+二分. N最大为2 ...