繁凡出品的全新系列:解题报告系列 —— 超高质量算法题单,配套我写的超高质量题解和代码,题目难度不一定按照题号排序,我会在每道题后面加上题目难度指数( 1 ∼ 5 1 \sim 5 1∼5),以模板题难度 1 1 1 为基准。
这样大家在学习算法的时候就可以执行这样的流程:
%
阅读我的【学习笔记】 / 【算法全家桶】学习算法 ⇒ \Rightarrow ⇒ 阅读我的相应算法的【解题报告】获得高质量题单 ⇒ \Rightarrow ⇒ 根据我的一句话题解的提示尝试自己解决问题 ⇒ \Rightarrow ⇒ 点开我的详细题解链接学习巩固(好耶)%
解题报告系列合集:【解题报告系列】超高质量题单 + 题解(ICPC / CCPC / NOIP / NOI / CF / AT / NC / P / BZOJ)
本期题单前置知识:【学习笔记】《概率与期望全家桶》(ACM / OI)概率与期望知识小结
目录
- 概率
- 期望
- A. UVA12230 过河 Crossing Rivers(期望)
- B. P4316 绿豆蛙的归宿(期望DP拓扑排序DP)
- C. P1850 [NOIP2016 提高组] 换教室(期望DP)
- D. P3232 [HNOI2013]游走(期望DP + 列方程高斯消元)
- E. AcWing 218. 扑克牌(简单期望DP)
- F. P1291 [SHOI2002]百事世界杯之旅(收集问题)
- G. P1654 OSU!(立方的期望)
- H. P3802 小魔女帕琪(期望 + 组合数学)
- I. 牛客挑战赛36 D. 排名估算( “概率论全家桶”,好题,拉格朗日插值求自然数 k 次幂之和)
- J. 牛客挑战赛38 C.圆桌聚餐(条件概率,贝叶斯公式,期望 dp,思维)
- K. ABC 199 F - Graph Smoothing(数学期望,图的邻接矩阵幂的意义,矩阵快速幂)
- L. 牛客挑战赛 38 D.突击检查(期望 + 思维)
- M. P4550 收集邮票(期望DP)
- N. UVA1639 Candy(防爆对指数 + 二项分布 + 期望)
- O. P4206 [NOI2005] 聪聪与可可 (记忆化搜索期望DP + SPFA预处理)
- P. P4745 [CERC2017]Gambling Guide(期望DP + 最短路)
- Q. UVA12177 First Knight(期望DP + 高斯消元 + 优化)
- R. CF24D Broken robot(期望DP + 高斯消元)
- S. UVA 10900 So you want to be a 2n-aire?(连续型概率,均匀分布)
- T. CF1139D Steps to One(期望,莫比乌斯反演,杜教筛)
- U. LightOJ 1274 Beating the Dataset(期望,贡献和思想)
- V. 牛客练习赛82 D. Mocha 的数据包
- W. HDU 4579 Random Walk(期望 + 高斯消元)
- X. HDU 6829 Borrow
- Y. HDU 6052 To my boyfriend(贡献和)
- Z. HDU 4870 Rating
- α. LightOJ 1151 Snakes and Ladders
- β. P4457 [BJOI2018]治疗之雨(期望 + 高斯消元)
- γ. P3600 随机数生成器
概率
A. UVA11722 Joining with Friend(几何概型)
Problem
两人会面,第一个人去的时间在[t1, t2]中,第二个人去的时间在[s1, s2]中,两人会面成功的话,到达会面地点的时间差不能大于w,求两人成功会面的概率。
Solution
设 a a a 在 x x x 时间到达, b b b 在 y y y 时间到达,即 ∣ y − x ∣ < = w |y-x|<=w ∣y−x∣<=w 时可以碰面。即 x − w < = y < = x + w x-w<=y<=x+w x−w<=y<=x+w
显然答案就是总面积减去阴影面积。
我们设上面的白色面积为s1,中间阴影面积s2,下面白色面积为s3,则概率为 s 2 s 1 + s 2 + s 3 = ( s 2 + s 1 ) − s 1 s 1 + s 2 + s 3 \cfrac{s2}{s1+s2+s3}=\cfrac{(s2+s1)-s1}{s1+s2+s3} s1+s2+s3s2=s1+s2+s3(s2+s1)−s1
即我们可以直接计算直线 y = x + w , y = x − w y=x+w,y=x-w y=x+w,y=x−w 切割的四边形上面交的面积,相减即可。
根据四边形形状长度的不同,直线 y = x + w , y = x − w y=x+w,y=x-w y=x+w,y=x−w 与四边形的交一共有六种情况:
分类讨论即可。
Time
O ( 1 ) O(1) O(1)
Code
// Problem: UVA11722 Joining with Friend
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/UVA11722
// Memory Limit: 0 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 50007;
int n, m, t;
double s1, s2, t1, t2, w;
int kcase;
double get_area(double b)
{
if(t1 + b >= s2) return 0;
if(t2 + b <= s1) return (t2 - t1) * (s2 - s1);
if(t1 + b >= s1 && t2 + b <= s2) return (t2 - t1) * (s2 - t2 - b + s2 - t1 - b) * 0.5;
if(t1 + b >= s1 && t2 + b > s2) return (s2 - b - t1) * (s2 - b - t1) * 0.5;
if(t1 + b <= s1 && t2 + b >= s2) return (s2 - s1) * (s1 - b - t1 + s2 - b - t1) * 0.5;
if(t1 + b < s1 && t2 + b < s2 && t2 + b > s1) return (t2 - t1) * (s2 - s1) - (t2 + b - s1) * (t2 + b - s1) * 0.5;
return 0;
}
void solve()
{
scanf("%lf%lf%lf%lf%lf", &t1, &t2, &s1, &s2, &w);
printf("Case #%d: ", ++ kcase);
double tot = (t2 - t1) * (s2 - s1);
double up = get_area(-w) - get_area(w);
printf("%.8f\n", up / tot);
}
int main()
{
scanf("%d", &t);
while(t -- ) {
solve();
}
return 0;
}
B. UVA11021 Tribles(概率DP)
Weblink
https://www.luogu.com.cn/problem/UVA11021
Problem
一开始有 k k k 个麻球,所有麻球只能活 1 1 1 天,死的时候有 p i p_i pi 的概率产生 i i i 只这种麻球(也只能活一天, 0 ≤ i < n 0\le i< n 0≤i<n),询问 m m m 天内所有麻球都死的概率(包括m天前死亡的情况)
Solution
显然每个麻球是相互独立的,我们可以设 f [ i ] f[i] f[i] 表示一个麻球第 i i i 天以内死光的概率,则 k k k 个麻球在第 i i i 天内死亡的概率是 f [ i ] k f[i]^k f[i]k 。
我们现在仅考虑一只麻球的情况,即该麻球第一天繁衍多少个,它们在接下来的 i − 1 i−1 i−1 天内死绝了。
根据全概率公式
P ( A ) = ∑ i = 1 n P ( A ∣ B i ) × P ( B i ) P(A)=\sum_{i=1}^{n}P(A|B_i)\times P(B_i) P(A)=i=1∑nP(A∣Bi)×P(Bi)
我们就可以设计转移方程:
f [ i ] = ∑ j = 0 n − 1 p [ j ] f [ i − 1 ] j f[i]=\sum_{j=0}^{n-1}p[j]f[i-1]^j f[i]=j=0∑n−1p[j]f[i−1]j
对于每一个麻球来说都有 n n n 种选择。其中 p j ( f i − 1 ) j p_{j}(f_{i-1})^{j} pj(fi−1)j 的表示,生了 j j j 个麻球,这些麻球在 i − 1 i-1 i−1 天内死光,即第一只 a a a 及其后代需要在 i i i 天内死光,那么第一只的直系后代 b b b 及其后代需要在 i − 1 i-1 i−1 天内全部死光,因为第一只需要活一天,则 b b b 的直系后代及其后代需要在 i − 2 i-2 i−2天全部死光( b b b 需要活了一天),即 f [ i ] f[i] f[i]由 f [ i − 1 ] f[i-1] f[i−1] 转移过来, f [ i − 1 ] f[i-1] f[i−1] 由 f [ i − 2 ] f[i-2] f[i−2] 转移过来。
因为 k k k 只麻球相互独立, 对于两个独立事件 A , B A,B A,B, P ( A B ) = P ( A ) × P ( B ) P(AB)=P(A)\times P(B) P(AB)=P(A)×P(B)
最后的答案就是 f [ m ] k {f[m]}^{k} f[m]k
Code
// Problem: UVA11021 Tribles
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/UVA11021
// Memory Limit: 0 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 5007;
int n, m, k, t;
int kcase;
double p[N], f[N];
void solve()
{
scanf("%d%d%d", &n, &k, &m);
for(int i = 0; i <= m; ++ i) f[i] = 0;
for(int i = 0; i < n; ++ i) {
scanf("%lf", &p[i]);
}
for(int i = 1; i <= m; ++ i) {
for(int j = 0; j < n; ++ j) {
f[i] += p[j] * pow(f[i - 1], j);
}
}
printf("%.7f\n", pow(f[m], k));
}
int main()
{
scanf("%d", &t);
while(t -- ) {
printf("Case #%d: ", ++ kcase);
solve();
}
return 0;
}
C. 算概率(简单概率DP)
Weblink
https://ac.nowcoder.com/acm/contest/3003/C
Problem
Solution
显然概率DP,设 f i , j f i,j fi,j 表示前 i i i 道题里做对 j j j 道的概率。转移时我们只需要考虑第 j j j 道题是否做对即可。
转移方程为:
f i , j = f i − 1 , j × ( 1 − p i ) + f i − 1 , j − 1 × p i f_{i,j}=f_{i-1,j}\times (1-p_i)+f_{i-1,j-1}\times p_i fi,j=fi−1,j×(1−pi)+fi−1,j−1×pi
Time
O ( n 2 ) O(n^2) O(n2)
Code
// Problem: 算概率
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/3003/C
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 5007, mod = 1e9 + 7;
int p[N], f[N][N];
int n, m;
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++ i) {
scanf("%d", &p[i]);
}
f[0][0] = 1;
for(int i = 1; i <= n; ++ i) {
f[i][0] = 1ll * f[i - 1][0] * (mod + 1 - p[i]) % mod;
for(int j = 1; j <= i; ++ j) {
f[i][j] = (1ll * f[i - 1][j] * (mod + 1 - p[i]) % mod + 1ll * f[i - 1][j - 1] * p[i] % mod) % mod;
}
}
for(int i = 0; i <= n; ++ i) {
printf("%d ", f[n][i]);
}
puts("");
return 0;
}
D. UVA1636 决斗 Headshot
Problem
Solution
Code
// Problem: UVA1636 决斗 Headshot
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/UVA1636
// Memory Limit: 0 MB
// Time Limit: 3000 ms00
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 50007;
int n, m;
string s;
int cnt00, cnt0, cnt1;
int main()
{
while(cin >> s) {
cnt00 = cnt0 = cnt1 = 0;
int len = s.length();
s += s[0];
for(int i = 0; i < len; ++ i) {
if(s[i] == '0')
cnt0 ++ ;
if(s[i] == '0' && s[i + 1] == '0')
cnt00 ++ ;
}
int shoot = cnt00 * (len);
int rotate = cnt0 * (cnt0);
if(shoot == rotate) {
puts("EQUAL");
}
else if(shoot < rotate) {
puts("ROTATE");
}
else puts("SHOOT");
}
return 0;
}
E. UVA10491 奶牛和轿车 Cows and Cars
Problem
电视节目:你面前有 3 个门,其中两扇门后有奶牛,另 外一扇门后则藏着奖品——一辆豪华小轿车。在你选择一扇门之后, 门并不会立即打开。这时,主持人会给你个提示,具体方法是打开其 中一扇有奶牛的门(不会打开你已经选择那个门,即使里面是牛)。 接下来你有两种可能的决策:保持先前的选择,或者换成另外一扇未开的门。当然,你最终选择打开的那扇门后面的东西就归你了。 在这个例子里,你能得到轿车的概率是 2/3(难以置信吧!),方法是总是改变自己的选择。2/3 这个数字是这样得到的:如果开始选择了两个牛之一,你肯定能换到车前面的门,因为主持人已经让你看了另外一个牛;而如果开始你选择是车,就会换成剩下的牛并且失去大奖。由于你最初的选择是任意的,因此选错的概率是 2/3。也正是这 2/3 的情况让你能赢得大奖小轿车(另外 1/3 的情况你会从车转变为牛)。 现在把问题推广一下,假设有 a 头牛,b 辆车(门的总数是 a+b, 每个门后一头牛或一辆车),在最终选择前主持人会替你打开 c 个有 牛的门( 1 ≤ a ≤ 10000 , 1 ≤ b ≤ 10000 , 0 ≤ c ≤ a 1\leq a \leq10000,1\leq b \leq10000, 0\leq c \leq a 1≤a≤10000,1≤b≤10000,0≤c≤a) ,输出 “总是换门” 策略下,赢得车的概率。
Solution
显然是一个古典概率,一共有两种互斥事件 A A A, B B B,所以分类讨论一下求和既是答案。 P ( A ∪ B ) = P ( A ) + P ( B ) P(A∪B)=P(A)+P(B) P(A∪B)=P(A)+P(B)。
Code
// Problem: UVA10491 奶牛和轿车 Cows and Cars
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/UVA10491
// Memory Limit: 0 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 60007;
int n, m;
double a, b, c;
int main()
{
while(scanf("%lf%lf%lf", &a, &b, &c) != EOF) {
double ans = (a * b + b * (b - 1)) / ((a + b) * (a + b - c - 1));
printf("%.5f\n", ans);
}
}
F. UVA11181 条件概率 Probability|Given
// Problem: UVA11181 条件概率 Probability|Given
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/UVA11181
// Memory Limit: 0 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 507;
int n, m, t, r;
double sum[N], tot;
double p[N];
bool vis[N];
int kcase;
void init()
{
tot = 0;
memset(vis, 0, sizeof vis);
memset(sum, 0, sizeof sum);
}
//cnt第几个人num选了几个人
void dfs(int cnt, int num, double prob)
{
if(num > r || n - cnt < r - num)
return ;
if(cnt == n) {
tot += prob;
for(int i = 0; i < n; ++ i) {
if(vis[i])
sum[i] += prob;
}
return ;
}
vis[cnt] = false;
dfs(cnt + 1, num, prob * (1 - p[cnt]));
vis[cnt] = true;
dfs(cnt + 1, num + 1, prob * p[cnt]);
}
int main()
{
while(scanf("%d%d", &n, &r) != EOF && n + r) {
init();
printf("Case %d:\n", ++ kcase);
for(int i = 0; i < n; ++ i) {
scanf("%lf", &p[i]);
}
dfs(0, 0, 1.0);
for(int i = 0; i < n; ++ i) {
printf("%.6f\n", sum[i] / tot);
}
}
return 0;
}
G. UVALive6672 Bonus Cards
Weblink
Problem
Solution
Code
H. HDU 5985 Lucky Coins
I. HDU 5955 Guessing the Dice Roll
J. HDU 5819 Knights
期望
A. UVA12230 过河 Crossing Rivers(期望)
Problem
Solution
显然对于每条河来说,有两种情况极端情况:
最好的情况:到达河边时船刚好到,花费时间 t = L v t= \cfrac{L}{v} t=vL 。
最坏的情况:到达河边时船刚好走,花费时间 t = 3 × L v t=3 \times \cfrac{L}{v} t=3×vL 。
由于船速度是匀速的,则根据期望的线性性质可得:每条河的平均过河时间 t = L v + 3 × L v 2 = 2 × L v t=\cfrac{\cfrac{L}{v}+3 \times \cfrac{L}{v}}{2}=2 \times \cfrac{L}{v} t=2vL+3×vL=2×vL 。期望为 E = t = 2 × L v E=t=2 \times \cfrac{L}{v} E=t=2×vL
显然总花费时间的期望为每条河的期望之和。
Code
// Problem: UVA12230 过河 Crossing Rivers
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/UVA12230
// Memory Limit: 0 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 50007;
int n, m;
int d, kcase;
double p, l, v;
int main()
{
while(scanf("%d%d", &n, &d) != EOF && n + d) {
printf("Case %d: ", ++ kcase);
double ans = 0;
for(int i = 1; i <= n; ++ i) {
scanf("%lf%lf%lf", &p, &l, &v);
ans += 2.0 * l / v;
d -= l;//减去河的宽度
}
printf("%.3f\n\n", ans + d);
}
}
B. P4316 绿豆蛙的归宿(期望DP拓扑排序DP)
Problem
Solution
懒得写题解了…
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 500007, M = 500007;
int t, n, m;
int head[N], ver[M], nex[M], edge[M], tot;
double dist[N];//从i到终点的期望距离
int out[N], deg[N];
void add(int x, int y, int z)
{
ver[tot] = y;
nex[tot] = head[x];
edge[tot] = z;
head[x] = tot ++ ;
}
void toposort()
{
queue <int> q;
q.push(n);
while(q.size()) {
int x = q.front();
q.pop();
for(int i = head[x]; ~i; i = nex[i]) {
int y = ver[i];
int z = edge[i];
dist[y] += (dist[x] + (double)z) / deg[y];
out[y] -- ;
if(out[y] == 0) q.push(y);
}
}
}
int main()
{
memset(head, -1, sizeof head);
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++ i) {
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
add(y, x, z);
deg[x] ++ ;
out[x] ++ ;
}
toposort();
printf("%.2f\n", dist[1]);
return 0;
}
C. P1850 [NOIP2016 提高组] 换教室(期望DP)
D. P3232 [HNOI2013]游走(期望DP + 列方程高斯消元)
https://www.luogu.com.cn/problem/P3232
E. AcWing 218. 扑克牌(简单期望DP)
Weblink
https://www.acwing.com/problem/content/220/
Problem
Solution
懒得写题解了
注意每次转移的时候需要翻开一张牌,所以期望次数 +1,所以我们转移的时候设 t = 1
。
即:
f [ a ] [ b ] [ c ] [ d ] [ x ] [ y ] = 13 − a S u m f [ a + 1 ] [ b ] [ c ] [ d ] [ x ] [ y ] + 13 − b S u m f [ a ] [ b + 1 ] [ c ] [ d ] [ x ] [ y ] + 13 − c S u m f [ a ] [ b ] [ c + 1 ] [ d ] [ x ] [ y ] + 13 − d S u m f [ a ] [ b ] [ c ] [ d + 1 ] [ x ] [ y ] + min 0 ≤ i ≤ 3 f [ a ] [ b ] [ c ] [ d ] [ i ] [ y ] ( x = = 4 ) + min 0 ≤ j ≤ 3 f [ a ] [ b ] [ c ] [ d ] [ x ] [ j ] ( y = = 4 ) + 1 \begin{aligned}f[a][b][c][d][x][y]&= \ \frac{13-a}{Sum}f[a+1][b][c][d][x][y]&\\& +\frac{13-b}{Sum}f[a][b+1][c][d][x][y]&\\ &+\frac{13-c}{Sum}f[a][b][c+1][d][x][y]&\\ &+\frac{13-d}{Sum}f[a][b][c][d+1][x][y]&\\& +\min_{0 \le i \le 3}{f[a][b][c][d][i][y]}(x==4)&\\ &+\min_{0 \le j \le3}{f[a][b][c][d][x][j]}(y==4) &\\&+ 1\end{aligned} f[a][b][c][d][x][y]= Sum13−af[a+1][b][c][d][x][y]+Sum13−bf[a][b+1][c][d][x][y]+Sum13−cf[a][b][c+1][d][x][y]+Sum13−df[a][b][c][d+1][x][y]+0≤i≤3minf[a][b][c][d][i][y](x==4)+0≤j≤3minf[a][b][c][d][x][j](y==4)+1
记忆化搜索即可。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 14, INF = 0x3f3f3f3f;
int n, m;
int A, B, C, D, x, y;
double f[N][N][N][N][5][5];
double dp(int a, int b, int c, int d, int x, int y)
{
double &t = f[a][b][c][d][x][y];
if(t >= 0) return t;
int as = a + (x == 0) + (y == 0);
int bs = b + (x == 1) + (y == 1);
int cs = c + (x == 2) + (y == 2);
int ds = d + (x == 3) + (y == 3);
if(as >= A && bs >= B && cs >= C && ds >= D) return t = 0;
double sum = a + b + c + d + (x != 4) + (y != 4);
sum = 54.0 - sum;
if(sum <= 0) return INF;
t = 1;//!
if(a < 13) t += (13.0 - a) / (sum) * dp(a + 1, b, c, d, x, y);
if(b < 13) t += (13.0 - b) / (sum) * dp(a, b + 1, c, d, x, y);
if(c < 13) t += (13.0 - c) / (sum) * dp(a, b, c + 1, d, x, y);
if(d < 13) t += (13.0 - d) / (sum) * dp(a, b, c, d + 1, x, y);
if(x == 4) {
double tmp = INF;
for(int i = 0; i <= 3; ++ i)
tmp = min(tmp, dp(a, b, c, d, i, y) / sum);
t += tmp;
}
if(y == 4) {
double tmp = INF;
for(int i = 0; i <= 3; ++ i)
tmp = min(tmp, dp(a, b, c, d, x, i) / sum);
t += tmp;
}
return t;
}
int main()
{
memset(f, -1, sizeof f);
scanf("%d%d%d%d", &A, &B, &C, &D);
double ans = dp(0, 0, 0, 0, 4, 4);
if(ans > INF / 2) puts("-1.000");
else printf("%.3f\n", ans);
return 0;
}
F. P1291 [SHOI2002]百事世界杯之旅(收集问题)
Problem
双倍经验:(SP1026 FAVDICE - Favorite Dice)一个 n n n 面的骰子,求期望掷几次能使得每一面都被掷到。
Input
17
Output
340463
58------
720720
Solution
我们设 f [ i ] f[i] f[i] 表示已经买到 i i i 个球星的期望购买次数。
考虑最后一次购买,若买到的是一个新球星,则:
f [ i ] + = ( f [ i − 1 ] + 1 ) × n − ( i − 1 ) n f[i] += (f[i-1]+1)\times \cfrac{n-(i-1)}{n} f[i]+=(f[i−1]+1)×nn−(i−1)
若买到的是一个已经买过但不是第 i i i 个买的球星,则:
f [ i ] + = ( f [ i ] + 1 ) × i − 1 n f[i]+=(f[i]+1)\times \frac{i-1}{n} f[i]+=(f[i]+1)×ni−1
整理一下得:
f [ i ] = f [ i − 1 ] + n n − i + 1 f[i]=f[i-1]+\frac{n}{n-i+1} f[i]=f[i−1]+n−i+1n
a n s = ∑ i = 1 n n n − i + 1 = ∑ i = 1 n n i ans = \sum_{i = 1}^{n}\cfrac{n}{n-i+1}=\sum_{i=1}^{n}\cfrac{n}{i} ans=i=1∑nn−i+1n=i=1∑nin
显然最后的答案就是调和级数前缀和。
若数据较大的话可以 O ( 1 ) O(1) O(1) 计算调和级数前缀和:
调和级数 ∑ i = 1 ∞ 1 n \displaystyle\sum_{i = 1}^{∞}\cfrac{1}{n} i=1∑∞n1 的极限为 l n n + C lnn+C lnn+C,其中 C = 0.57721566490153286060651209 C=0.57721566490153286060651209 C=0.57721566490153286060651209 是欧拉常数
const int N = 500007;
double f[N];
int n;
int main()
{
for(int i = 1; i <= N - 1; ++ i)
f[i] = f[i - 1] + 1.0 / i;
while(scanf("%d", &n) != EOF) {
if(n > N - 7)
printf("%.4f\n", 0.57721566490153286060651209 + log(n + 1));
else printf("%.4f\n", f[n]);
}
return 0;
}
Code
// Problem: P1291 [SHOI2002]百事世界杯之旅
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1291
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define int long long
using namespace std;
//#define ll __int128;
typedef long long ll;
const int N = 107;
int n, m;
ll lcm(int a, int b)
{
return a / __gcd(a, b) * b;
}
int get_len(int x)
{
int len = 0;
while(x) {
x /= 10;
len ++ ;
}
return len;
}
signed main()
{
scanf("%lld", &n);
ll LCM = 1;
for(int i = 1; i <= n; ++ i) {
LCM = lcm(LCM, i);
}
ll sum = 0;
for(int i = 1; i <= n; ++ i) {
sum += n * (LCM / i);
}
ll d = __gcd(sum, LCM);
sum /= d, LCM /= d;
if(LCM == 1) {
cout << sum << endl;
return 0;
}
ll mo = sum % LCM;
ll l = sum / LCM;
for(int i = 1; i <= get_len(l); ++ i) cout << " ";
cout << mo << endl;
cout << l;
for(int i = 1; i <= get_len(LCM); ++ i) cout << "-";
puts("");
for(int i = 1; i <= get_len(l); ++ i) cout << " ";
cout << LCM << endl;
return 0;
}
G. P1654 OSU!(立方的期望)
Problem
Solution
设 a i a_i ai 表示前 i i i 位中第 i i i 位为 1 1 1 的长度的期望, b i b_i bi 表示前 i i i 位中第 i i i 位为 1 1 1 的长度的平方的期望, c i c_i ci 表示前 i i i 位中第 i i i 位为 1 1 1 的长度的立方的期望。
则有:
a [ i ] = ( a [ i − 1 ] + 1 ) × p [ i ] a[i]=(a[i-1]+1)\times p[i] a[i]=(a[i−1]+1)×p[i]
即第 i i i 位以 p [ i ] p[i] p[i] 的概率为 1 1 1,由第 i − 1 i-1 i−1 位同样为 1 1 1 的长度的期望 + 1 +1 +1 而来。
b [ i ] = ( b [ i − 1 ] + 2 × a [ i − 1 ] + 1 ) × p [ i ] b[i]=(b[i-1]+2\times a[i-1]+1)\times p[i] b[i]=(b[i−1]+2×a[i−1]+1)×p[i]
即: ( x + 1 ) 2 = x 2 + 2 x + 1 (x+1)^2=x^2+2x+1 (x+1)2=x2+2x+1
同理有:
c [ i ] = ( c [ i − 1 ] + 3 × b [ i − 1 ] + 3 × a [ i − 1 ] + 1 ) × p [ i ] c[i]=(c[i-1]+3\times b[i-1]+3\times a[i-1]+1)\times p[i] c[i]=(c[i−1]+3×b[i−1]+3×a[i−1]+1)×p[i]
即 a , b , c a,b,c a,b,c 的前提都是一样的 :连续的 1 1 1。
最后答案是期望长度的立方 ,而这里 c [ i ] c[i] c[i] 是指第 i i i 位为 1 1 1 的长度的期望,还有第 i i i 位为 0 0 0 的情况,故:
c [ i ] = ( c [ i − 1 ] + 3 × b [ i − 1 ] + 3 × a [ i − 1 ] + 1 ) × p [ i ] + c [ i − 1 ] × ( 1 − p [ i ] ) = c [ i − 1 ] + ( 3 × b [ i − 1 ] + 3 × a [ i − 1 ] + 1 ) × p [ i ] \begin{aligned}c[i] &= (c[i-1]+3\times b[i-1]+3\times a[i-1]+1)\times p[i] + c[i-1]\times(1-p[i])&\\&=c[i-1]+(3\times b[i-1]+3\times a[i-1]+1)\times p[i]\end{aligned} c[i]=(c[i−1]+3×b[i−1]+3×a[i−1]+1)×p[i]+c[i−1]×(1−p[i])=c[i−1]+(3×b[i−1]+3×a[i−1]+1)×p[i]
Code
// Problem: P1291 [SHOI2002]百事世界杯之旅
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1291
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 500007;
int n, m;
double a[N], b[N], c[N], p[N];
double ans;
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++ i) {
scanf("%lf", &p[i]);
a[i] = (a[i - 1] + 1) * p[i];
b[i] = (b[i - 1] + 2 * a[i - 1] + 1) * p[i];
c[i] = c[i - 1] + (3 * b[i - 1] + 3 * a[i - 1] + 1) * p[i];
}
printf("%.1f\n", c[n]);
return 0;
}
三倍经验:
https://www.luogu.com.cn/problem/P1365
https://www.luogu.com.cn/problem/CF235B
H. P3802 小魔女帕琪(期望 + 组合数学)
Problem
Solution
Code
// Problem: P3802 小魔女帕琪
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3802
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 50007;
double a[N];
int n;
int main()
{
double fact7 = 1;
double sum = 0;
double ans;
for(int i = 1; i <= 7; ++ i)
scanf("%lf", &a[i]), sum += a[i], fact7 *= i;
ans = fact7 * a[1] / sum * a[2] / (sum - 1) * a[3] / (sum - 2) * a[4] / (sum - 3) * a[5] / (sum - 4) * a[6] / (sum - 5) * a[7];
printf("%.3f\n", ans);
return 0;
}
I. 牛客挑战赛36 D. 排名估算( “概率论全家桶”,好题,拉格朗日插值求自然数 k 次幂之和)
Weblink
https://ac.nowcoder.com/acm/contest/3782/D
Problem
期末考后,小 C 登录了学校的成绩查询系统,却发现自己的排名被屏蔽了。为了知道自己的排名,小 C 使用了系统中的“好友伴学”功能。每次,系统会在除了小 C 之外的所有考生中随机抽取一名,然后返回 Ta 的排名比小 C 高还是低。这次考试有 n n n 个人参加,小 C 总共使用的 m m m 次 “好友伴学” 功能,却没有一次抽中排名比自己高的人。请问小C在这次考试中的期望排名是多少?
2 ≤ n ≤ 1 0 11 , 0 ≤ m ≤ 5000 2≤n≤10^{11},0≤m≤5000 2≤n≤1011,0≤m≤5000
假设小C的的期望排名化为最简分数后是 p / q p/q p/q,输出 p ∗ q − 1 m o d 998244353 p*q^{-1}\mod 998244353 p∗q−1mod998244353。
Solution
设 p i p_i pi 表示排名为第 n − i n -i n−i 时, m m m 次没抽中排名比自己高的人(排名高指排名在自己前面hhh)的概率。
显然排名只有 1 ∼ n 1\sim n 1∼n,则对于 0 ≤ i ≤ n − 1 0\le i\le n-1 0≤i≤n−1, p i = ( i n − 1 ) m p_{i}=(\cfrac{i}{n-1})^{m} pi=(n−1i)m(排名 n − i n-i n−i,则比自己排名高的一共有 n − i n-i n−i 个人,没有抽中显然是抽到了自己后面的 i i i 排名低的人)
令事件 A A A 为 m m m 次没抽中,事件 B i B_i Bi 为排名为 n − i n-i n−i 。
根据乘法公式: P ( A B i ) = P ( A ∣ B i ) × P ( B i ) = P ( B i ∣ A ) × P ( A ) P(AB_i)=P(A|B_i)\times P(B_i)=P(B_i|A)\times P(A) P(ABi)=P(A∣Bi)×P(Bi)=P(Bi∣A)×P(A),
我们首先计算 P ( A ) P(A) P(A),根据全概率公式:
P ( A ) = ∑ i = 0 n − 1 P ( B i ) × P ( A ∣ B i ) P(A)=\sum\limits_{i=0}^{n-1}P(B_i)\times P(A|B_i) P(A)=i=0∑n−1P(Bi)×P(A∣Bi)
即排名为 i i i 的前提下事件 A A A , m m m 次没抽中的概率之和。
显然
P ( B i ) = 1 n , P ( A ∣ B i ) = p i = ( i n − 1 ) m P(B_i)=\frac{1}{n},P(A|B_i)=p_i=(\frac{i}{n-1})^{m} P(Bi)=n1,P(A∣Bi)=pi=(n−1i)m
则
P ( A ) = ∑ i = 0 n − 1 ( i n − 1 ) m × 1 n P(A)=\sum_{i=0}^{n-1}(\frac{i}{n-1})^{m} \times \frac{1}{n} P(A)=i=0∑n−1(n−1i)m×n1
代入贝叶斯公式:
P ( B i ∣ A ) = P ( B i ) × P ( A ∣ B i ) ∑ j = 0 n − 1 P ( B j ) × P ( A ∣ B j ) = p i × 1 n ∑ j = 0 n − 1 p j × 1 n \begin{aligned}P(B_i|A)&=\frac{\displaystyle P(B_i)\times P(A|B_i)}{\displaystyle\sum\limits_{j=0}^{n-1}P(B_j)\times P(A|B_j)}&\\&=\cfrac{p_i\times \dfrac{1}{n}}{\displaystyle\sum_{j=0}^{n-1}p_j\times \frac{1}{n}}\end{aligned} P(Bi∣A)=j=0∑n−1P(Bj)×P(A∣Bj)P(Bi)×P(A∣Bi)=j=0∑n−1pj×n1pi×n1
期望 E ( x ) = P × x E(x)=P\times x E(x)=P×x
x x x 是排名, p p p 为排名为 x x x 时抽 m m m 次抽不到的概率。
显然答案为:
a
n
s
=
∑
x
=
1
n
E
(
x
)
=
∑
x
=
1
n
P
(
B
i
∣
A
)
×
(
n
−
i
)
=
∑
i
=
0
n
−
1
p
i
×
1
n
×
(
n
−
i
)
∑
j
=
0
n
−
1
p
j
×
1
n
=
∑
i
=
0
n
−
1
p
i
×
(
n
−
i
)
∑
i
=
0
n
−
1
p
i
=
n
−
∑
i
=
0
n
−
1
i
m
+
1
∑
i
=
0
n
−
1
i
m
\begin{aligned}ans & = \sum_{x=1}^{n}E(x)& \\ & = \sum_{x=1}^{n} P(B_i|A) \times (n - i)&\\&=\sum_{i=0}^{n-1}\cfrac{p_i\times \dfrac{1}{n}\times (n-i)}{\displaystyle\sum_{j=0}^{n-1}p_j\times \frac{1}{n}}&\\&=\displaystyle \frac{\displaystyle \sum_{i=0}^{n-1}p_{i}\times(n-i)}{\displaystyle \sum_{i=0}^{n-1}p_{i}}&\\&=n-\cfrac{\displaystyle\sum_{i=0}^{n-1}i^{m+1}}{\displaystyle\sum_{i=0}^{n-1}i^m}\end{aligned}
ans=x=1∑nE(x)=x=1∑nP(Bi∣A)×(n−i)=i=0∑n−1j=0∑n−1pj×n1pi×n1×(n−i)=i=0∑n−1pii=0∑n−1pi×(n−i)=n−i=0∑n−1imi=0∑n−1im+1
显然答案就是一个自然数 k k k 次幂之和,我们使用拉格朗日插值 O ( n ) O(n) O(n) 计算即可。
特判一下 m = 0 m=0 m=0 的情况,即不抽,那么期望就是:
E ( x ) = P × x = 1 n × ∑ i = 1 n i = 1 n × n ( n + 1 ) 2 = n + 1 2 E(x)=P\times x=\displaystyle\cfrac{1}{n}\times\sum\limits_{i=1}^{n}i=\cfrac{1}{n}\times \cfrac{n(n+1)}{2}=\cfrac{n+1}{2} E(x)=P×x=n1×i=1∑ni=n1×2n(n+1)=2n+1
当然不需要化简为最简分数,因为除法转换成逆元乘起来是等价的。
Code
// Problem: 排名估算
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/3782/D
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5007, M = 5007, mod = 998244353;
int read(){
int s = 0, ne = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') ne = -1; c = getchar();}
while(c >= '0' && c <= '9') s = (s << 1) + (s << 3) + c - '0', c = getchar();
return s * ne;
}
int qpow(int a, int b)
{
int res = 1;
while(b) {
if(b & 1) res = 1ll * res * a % mod;
a = 1ll * a * a % mod;
b >>= 1;
}
return res;
}
int s[N], pre[N], suf[N], infact[N], fact[N];
void init(int k)
{
infact[0] = fact[0] = 1;
for(int i = 1; i <= k + 10; ++ i)
fact[i] = 1ll * fact[i - 1] * i % mod;
infact[k + 10] = qpow(fact[k + 10], mod - 2);
for(int i = k + 9; i >= 0; -- i)
infact[i] = 1ll * infact[i + 1] * (i + 1) % mod;
infact[0] = 1;
}
int lagrange(int n, int k)
{
int ans = 0;
s[0] = 0;
for(int i = 1; i <= k + 2; ++ i)
s[i] = (s[i - 1] + qpow(i, k)) % mod;
if(n <= k + 1)
return s[n];
pre[0] = 1;
for(int i = 1; i <= k + 2; ++ i)
pre[i] = 1ll * pre[i - 1] * ((n - i + mod) % mod) % mod;
suf[k + 3] = 1;
for(int i = k + 2; i; -- i)
suf[i] = 1ll * suf[i + 1] * ((n - i + mod) % mod) % mod;
for(int i = 0; i <= k + 2; ++ i) {
s[i] = 1ll * s[i] * pre[i - 1] % mod * suf[i + 1] % mod * infact[i - 1] % mod * infact[k + 2 - i] % mod;
if((k + 2 - i) & 1)
ans = (1ll * ans - s[i] + mod) % mod;
else ans = (1ll * ans + s[i]) % mod;
}
return (ans + mod) % mod;
}
int n, m;
signed main()
{
init(M - 5);
scanf("%lld%lld", &n, &m);
if(m == 0) {
printf("%lld\n", (n + 1) % mod * qpow(2, mod - 2) % mod);
return 0;
}
int up = lagrange(n - 1, m + 1);
int down = lagrange(n - 1, m);
down = qpow(down, mod - 2);
printf("%lld\n", (n - up * down % mod + mod) % mod);
}
J. 牛客挑战赛38 C.圆桌聚餐(条件概率,贝叶斯公式,期望 dp,思维)
https://blog.csdn.net/qq_41997978/article/details/105053785?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-1&spm=1001.2101.3001.4242
Problem
Solution
Code
K. ABC 199 F - Graph Smoothing(数学期望,图的邻接矩阵幂的意义,矩阵快速幂)
Weblink
https://atcoder.jp/contests/abc199/tasks/abc199_f
Problem
Solution
我们要知道图的邻接矩阵的幂的意义,详见 P3758 [TJOI2017]可乐
解题报告(九) 矩阵与行列式(ACM / OI)超高质量题解
这个图就非常形象:
读题之后发现显然就是建图然后求 k k k 次幂即可,因为有 k k k 次互不影响的操作嘛。
我们拥有的操作是等概率地选择一条边,将这条边连接的两个点的点权变成两个点的点权的平均值。显然很难维护每一条边连接的两个点的平均值,所以我们换个思想,仅需维护每一个点得到的贡献即可。
显然对于每一个点的期望权值,只有自己对自己的贡献(概率),以及自己所连向的点对自己的贡献。
我们设 d i d_i di 为点 i i i 的度数。
首先考虑自己对自己的贡献:
那么对于一个点来说,不做任何操作的话对自己的贡献即为自己的点权 a i a_i ai,但是当我选择一条边的时候,每一个点一共连有 d i d_i di 条边,所以每一个点被选中的概率显然为 d i m \cfrac{d_i}{m} mdi,那么一个点被选中之后,它自己的贡献就从 a i a_i ai 变成了 a i 2 \cfrac{a_i}{2} 2ai(因为我们进行的是取平均数的操作),所以原来的期望为 1 × a i 1\times a_i 1×ai ,每次有 d i m \cfrac{d_i}{m} mdi 的概率选到点 i i i,被选择一次期望权值就减少 a i 2 \cfrac{a_i}{2} 2ai,即 贡献为 1 − d i 2 × m = 2 × m − d i 2 × m 1-\cfrac{d_i}{2\times m}=\cfrac{2\times m-d_i}{2\times m} 1−2×mdi=2×m2×m−di。自己( i i i)对自己( i i i)的贡献就是邻接矩阵里的 a [ i ] [ i ] a[i][i] a[i][i]。
然后考虑其他与之相连的点对自己的贡献:
那么对于任意一条边 ⟨ u , v ⟩ ⟨u,v⟩ ⟨u,v⟩,考虑点 u u u 对点 v v v 的贡献,显然选择到该边的概率为 1 m \cfrac{1}{m} m1, u u u 给 v v v 的贡献就是平均数 1 2 \cfrac{1}{2} 21。别人( j j j)给自己( i i i)的贡献就是邻接矩阵里的 a [ j ] [ i ] a[j][i] a[j][i]。
我们计算出每一个点得到的贡献(概率)之后,自己对自己的贡献乘上自己本身的点权,别人对自己的贡献(概率)乘上别人的点权就是每个点在 k k k 次操作后的点权的期望。
Time
O
(
n
3
l
o
g
k
)
O(n^3logk)
O(n3logk)
Code
// Problem: F - Graph Smoothing
// Contest: AtCoder - AtCoder Beginner Contest 199(Sponsored by Panasonic)
// URL: https://atcoder.jp/contests/abc199/tasks/abc199_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const int N = 507, mod = 1e9 + 7;
int res[N][N];
int a[N][N];
int val[N];
int e[N][N];
int n, m, k;
int d[N];
void mul(int c[], int a[], int b[][N])
{
int tmp[N] = {0};
for(int j = 1; j <= n; ++ j)
for(int k = 1; k <= n; ++ k)
tmp[j] = (tmp[j] + a[k] * b[k][j]) % mod;
memcpy(c, tmp, sizeof tmp);
}
void mul(int c[][N], int a[][N], int b[][N])
{
int tmp[N][N] = {0};
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= n; ++ j)
for(int k = 1; k <= n; ++ k)
tmp[i][j] = (tmp[i][j] + a[i][k] * b[k][j]) % mod;
memcpy(c, tmp, sizeof tmp);
}
int qpow(int a, int b)
{
int res = 1;
while(b) {
if(b & 1) res = 1ll * res * a % mod;
a = 1ll * a * a % mod;
b >>= 1;
}
return res;
}
signed main()
{
scanf("%lld%lld%lld", &n, &m, &k);
for(int i = 1; i <= n; ++ i)
scanf("%lld", &val[i]);
for(int i = 1; i <= m; ++ i) {
int x, y;
scanf("%lld%lld", &x, &y);
e[x][y] = e[y][x] = 1;
d[x] ++ ;
d[y] ++ ;
}
int inv_2m = qpow(2 * m, mod - 2);
for(int i = 1; i <= n; ++ i) {
for(int j = 1; j <= n; ++ j) {
if(e[i][j]) {
a[j][i] = inv_2m;//别人给自己的贡献(j 给 i 的贡献)
}
}//自己给自己的贡献(i 给 i 的贡献)
a[i][i] = (2 * m - d[i]) * inv_2m % mod;
}
for(int i = 1; i <= n; ++ i)
res[i][i] = 1;
while(k) {
if(k & 1) mul(res, res, a);
mul(a, a, a);
k >>= 1;
}
for(int i = 1; i <= n; ++ i) {
ll ans = 0;
for(int j = 1; j <= n; ++ j)
ans = (ans + (1ll * res[j][i] * val[j]) % mod) % mod;
cout << ans << endl;
}
return 0;
}
L. 牛客挑战赛 38 D.突击检查(期望 + 思维)
Problem
Solution
M. P4550 收集邮票(期望DP)
Weblink
https://www.luogu.com.cn/problem/P4550
Problem
有 n n n 种不同的邮票,皮皮想收集所有种类的邮票。唯一的收集方法是到同学凡凡那里购买,每次只能买一张,并且买到的邮票究竟是 n n n 种邮票中的哪一种是等概率的,概率均为 1 n \cfrac{1}{n} n1 。但是由于凡凡也很喜欢邮票,所以皮皮购买第 k k k 次邮票需要支付 k k k 元钱。
现在皮皮手中没有邮票,皮皮想知道自己得到所有种类的邮票需要花费的钱数目的期望。
Solution
Code
N. UVA1639 Candy(防爆对指数 + 二项分布 + 期望)
Weblink
https://www.luogu.com.cn/problem/UVA1639
Problem
有两个盒子各有n (n<=2e5) 个糖,每天随机选一个(概率分别为p,1-p),然后吃一颗糖。直到有一天,打开盒子一看,没糖了!输入n,p,求此时另一个盒子里糖的个数的数学期望。
Solution
显然是二项分布,当我们发现一个盒子没糖的时候,也就意味着我们是第 n + 1 n+1 n+1 次打开这个糖果盒子,假设设个盒子是第一个盒子,即选择一次它的概率为 p p p ,那么另一个糖果盒的概率为 1 − p 1-p 1−p 显然第二个盒子打开的次数不一定,可以是 i = 0 , 1 , 2 , 3 , 4 , 5 ⋯ n i=0,1,2,3,4,5\cdots n i=0,1,2,3,4,5⋯n,即概率为: P a = C 2 n − i n p n + 1 ( 1 − p ) n − i P_a = C_{2n-i}^{\ n}p^{n+1}(1-p)^{n-i} Pa=C2n−i npn+1(1−p)n−i
若没有糖果的盒子是第二个盒子,则概率为: P b = C 2 n − i n ( 1 − p ) n + 1 ( p ) n − i P_b = C_{2n-i}^{\ n}(1-p)^{n+1}(p)^{n-i} Pb=C2n−i n(1−p)n+1(p)n−i
则期望为 E X = ∑ i = 0 n ( i × ( P a + P b ) ) EX = \sum_{i = 0}^{n}{(i\times (P_{a}+P_{b}))} EX=∑i=0n(i×(Pa+Pb))
但是 n ≤ 2 × 1 0 5 n\le 2\times 10^5 n≤2×105,直接计算组合数的话没有给模数是一定会炸的,所以我们可以使用对指技巧,计算它的对数,然后求它的指数即可。
即:
ln
P
a
=
ln
(
C
2
n
−
i
n
∗
p
n
+
1
∗
(
1
−
p
)
n
−
i
)
ln
P
b
=
ln
C
2
n
−
i
n
+
ln
p
n
+
1
+
ln
(
1
−
p
)
n
−
i
ln
P
a
=
ln
(
2
n
−
i
)
!
−
ln
n
!
−
ln
(
n
−
i
)
!
+
(
n
+
1
)
ln
p
+
(
n
−
i
)
ln
(
1
−
p
)
ln
P
b
=
ln
(
2
n
−
i
)
!
−
ln
n
!
−
ln
(
n
−
i
)
!
+
(
n
+
1
)
ln
(
1
−
p
)
+
(
n
−
i
)
ln
p
E
X
=
∑
i
=
1
n
(
i
∗
(
e
ln
P
a
+
e
ln
P
b
)
)
\begin{array}{c}&\ln P_a =\ln \left(C_{2 n-i}^{n} * p^{n+1} *(1-p)^{n-i}\right) &\\&\ln P_b=\ln C_{2 n-i}^{n}+\ln p^{n+1}+\ln (1-p)^{n-i} &\\&\ln P_a=\ln (2 n-i) !-\ln n !-\ln (n-i) !+(n+1) \ln p+(n-i) \ln (1-p) &\\&\ln P_b=\ln (2 n-i) !-\ln n !-\ln (n-i) !+(n+1) \ln (1-p)+(n-i) \ln p &\\&\qquad E X=\sum_{i=1}^{n}\left(i *\left(e^{\ln P_a}+e^{\ln P_b}\right)\right)\end{array}
lnPa=ln(C2n−in∗pn+1∗(1−p)n−i)lnPb=lnC2n−in+lnpn+1+ln(1−p)n−ilnPa=ln(2n−i)!−lnn!−ln(n−i)!+(n+1)lnp+(n−i)ln(1−p)lnPb=ln(2n−i)!−lnn!−ln(n−i)!+(n+1)ln(1−p)+(n−i)lnpEX=∑i=1n(i∗(elnPa+elnPb))
我们可以先预处理一下
n
!
n!
n! 的对数,注意这里要用 long double
来存,否则会炸精度。
Code
// Problem: UVA1639 糖果 Candy
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/UVA1639
// Memory Limit: 0 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
using ld = long double;
const int N = 2e5 + 7;
int n, m;
ld p;
int t;
ld f[N << 1];
void init(int n)
{
for(int i = 1; i <= 400000; ++ i) {
f[i] = f[i - 1] + (ld)log(i);
}
}
int kcase;
int main()
{
init(N - 7);
while(scanf("%d%Lf", &n, &p) != EOF) {
printf("Case %d: ", ++ kcase);
ld EX = 0.0;
ld P = log(p);
ld Q = log(1 - p);
for(int i = 0; i <= n; ++ i) {
ld w1 = f[2 * n - i] - f[n] - f[n - i] + (n + 1) * Q + (n - i) * P;
ld w2 = f[2 * n - i] - f[n] - f[n - i] + (n + 1) * P + (n - i) * Q;
EX += i * (exp(w1) + exp(w2));
}
printf("%.6Lf\n", EX);
}
return 0;
}
O. P4206 [NOI2005] 聪聪与可可 (记忆化搜索期望DP + SPFA预处理)
Weblink
https://www.luogu.com.cn/problem/P4206
Problem
在一个魔法森林里,住着一只聪明的小猫聪聪和一只可爱的小老鼠可可。
整个森林可以认为是一个无向图,图中有
N
N
N 个美丽的景点,景点从
1
1
1 至
N
N
N
编号。在景点之间有一些路连接。
可可正在景点
M
(
M
≤
N
)
M (M ≤ N)
M(M≤N) 处。以后的每个时间单位,可可都会选择去相邻
的景点(可能有多个)中的一个或停留在原景点不动。而去这些地方所发生的概
率是相等的。
聪聪是很聪明的,所以,当她在景点 C 时,她会选一个更靠近可可的景点,
如果这样的景点有多个,她会选一个标号最小的景点。如果走完第一步以后仍然
没吃到可可,她还可以在本段时间内再向可可走近一步。
在每个时间单位,假设聪聪先走,可可后走。在某一时刻,若聪聪和可可位
于同一个景点,则可可就被吃掉了。
问平均情况下,聪聪几步就可能吃到可可。
Solution
根据题目要求聪聪会向可可不断靠近,且边无权,所以先设边权为 1 1 1 ,对于每一个点进行一次 SPFA ,预处理出 p [ i , j ] p[i, j] p[i,j] ,表示顶点 i i i 到顶点 j j j 的最短路上与顶点 i i i 相邻且编号最小的顶点编号。即聪聪在景点 i i i,可可在景点 j j j 时,聪聪第 1 1 1 步会走到的景点编号。
设 f [ i , j ] f[i, j] f[i,j] 来表示聪聪在顶点 i i i,可可在顶点 j j j 时聪聪抓住可可的平均步数,即期望步数。令 w [ i , j ] w[i, j] w[i,j] 表示与顶点 i i i 相邻的 j j j 个点编号,而用 d [ i ] d[i] d[i] 表示顶点 i i i 的度数。
显然聪聪下一步所在的顶点即为 p [ p [ i , j ] , j ] p[p[i, j], j] p[p[i,j],j],可可下一步在顶点 w [ i , j ] w[i, j] w[i,j] 的概率为 1 d [ i ] + 1 \cfrac{1}{d[i] + 1} d[i]+11,下一步这个情况下的期望 f [ p [ p [ i , j ] , j ] , w [ i , j ] ] f [p[p[i, j], j],w[i, j]] f[p[p[i,j],j],w[i,j]] 已经计算出,那么就是比 f [ p [ p [ i , j ] , j ] , w [ i , j ] ] f [p[p[i, j], j], w[i, j]] f[p[p[i,j],j],w[i,j]] 多出一步。可可在原地停留的情况则类似。
有转移方程:
f [ i , j ] = ( ∑ k = 1 t [ i ] f [ p [ p [ i , j ] , j ] , w [ j , k ] ] + 1 ) + f [ p [ p [ i , j ] , j ] , j ] + 1 d [ i ] + 1 + 1 = ( ∑ k = 1 t [ i ] f [ p [ p [ i , j ] , j ] , w [ j , k ] ] ) + f [ p [ p [ i , j ] , j ] , j ] + d [ i ] + 1 d [ i ] + 1 = ( ∑ k = 1 t [ i ] f [ p [ p [ i , j ] , j ] , w [ j , k ] ] ) + f [ p [ p [ i , j ] , j ] , j ] d [ i ] + 1 + 1 \begin{aligned}f[i, j]&=\frac{(\sum\limits_{k=1}^{t[i]} f[p[p[i, j], j], w[j, k]]+1)+f[p[p[i, j], j], j]+1}{d[i]+1}+1&\\&=\frac{(\sum\limits_{k=1}^{t[i]} f[p[p[i, j], j], w[j, k]])+f[p[p[i, j], j], j]+d[i]+1}{d[i]+1}&\\&=\frac{(\sum\limits_{k=1}^{t[i]} f[p[p[i, j], j], w[j, k]])+f[p[p[i, j], j], j]}{d[i]+1}+1\end{aligned} f[i,j]=d[i]+1(k=1∑t[i]f[p[p[i,j],j],w[j,k]]+1)+f[p[p[i,j],j],j]+1+1=d[i]+1(k=1∑t[i]f[p[p[i,j],j],w[j,k]])+f[p[p[i,j],j],j]+d[i]+1=d[i]+1(k=1∑t[i]f[p[p[i,j],j],w[j,k]])+f[p[p[i,j],j],j]+1
-
若 i = = j i == j i==j,则 f [ i , i ] = 0 f [i, i] = 0 f[i,i]=0 。
-
若 p [ p [ i , j ] , j ] = j p[p[i, j], j] = j p[p[i,j],j]=j 或 p [ i , j ] = j p[i, j]= j p[i,j]=j 则说明在这一步聪聪即可吃掉可可,那么 f [ i , j ] = 1 f [i, j]=1 f[i,j]=1。
记忆化搜索即可。
Code
// Problem: P4206 [NOI2005] 聪聪与可可
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4206
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 7, M = 2e3 + 7, INF = 0x3f3f3f3f;
int n, m, s, t;
int head[N], edge[M], ver[M], nex[M], tot;
bool vis[N];
int p[N][N];
int dist[N][N];
double f[N][N];
int d[N];
void add(int x, int y, int z)
{
ver[tot] = y;
edge[tot] = z;
nex[tot] = head[x];
head[x] = tot ++ ;
d[x] ++ ;
}
void SPFA(int s)
{
memset(dist, -1, sizeof dist);
vis[s] = 1;
dist[s][s] = 0;
queue<int> q;
q.push(s);
while(q.size()) {
int x = q.front();
q.pop();
vis[x] = 0;
for(int i = head[x]; ~i; i = nex[i]) {
int y = ver[i];
int z = edge[i];
if(dist[s][y] == -1 || (dist[s][y] == dist[s][x] + z && p[s][x] < p[s][y])) {
dist[s][y] = dist[s][x] + z;
if(p[s][x]) {
p[s][y] = p[s][x];
}
else p[s][y] = y;
if(vis[y] == 0) {
vis[y] = 1;
q.push(y);
}
}
}
}
}
//记忆化搜索
double dfs(int x, int y)
{
if(f[x][y]) {
return f[x][y];
}
if(x == y){
return f[x][y] = 0.0;
}
if(p[x][y] == y || p[p[x][y]][y] == y) {
return f[x][y] = 1.0;
}
for(int i = head[y]; ~i; i = nex[i]) {
int nex_node = ver[i];
f[x][y] += dfs(p[p[x][y]][y], nex_node);
}
f[x][y] = (f[x][y] + dfs(p[p[x][y]][y], y)) / (d[y] + 1) + 1;
return f[x][y];
}
double ans;
int main()
{
memset(head, -1, sizeof head);
scanf("%d%d", &n, &m);
scanf("%d%d", &s, &t);
for(int i = 1; i <= m; ++ i) {
int x, y;
scanf("%d%d", &x, &y);
add(x, y, 1);
add(y, x, 1);
}
for(int i = 1; i <= n; ++ i)
SPFA(i);
ans = dfs(s, t);
printf("%.3f\n", ans);
return 0;
}
P. P4745 [CERC2017]Gambling Guide(期望DP + 最短路)
Weblink
https://www.luogu.com.cn/problem/P4745
Problem
Solution
显然考虑期望 d p dp dp 。因为只有一个终点 n n n,且状态已知,考虑逆推。
设 f [ x ] f[x] f[x] 表示点 x x x 到 n n n 的期望花费,或者可以说是从 n n n 到点 x x x 的期望花费。 d [ x ] d[x] d[x] 表示 x x x 点的度数,则有
f [ x ] = ∑ min { f [ x ] , f [ y ] } d [ x ] + 1 \displaystyle f[x]=\frac{ \sum \min \{ f[x],f[y] \} }{d[x]}+1 f[x]=d[x]∑min{f[x],f[y]}+1
即,花费一枚硬币,选择原地不动,或者从 y y y 走到 x x x ,到达点 x x x 一共有 d [ x ] d[x] d[x] 条路,即一共有 d [ x ] d[x] d[x] 种情况,所以概率期望就是 1 d [ x ] \cfrac{1}{d[x]} d[x]1,花费的期望就为 总花费乘上 1 d [ x ] \cfrac{1}{d[x]} d[x]1。
观察公式显然可以发现对于一个点 x x x ,所有与 x x x 相邻的点 y y y,能对 x x x 产生贡献,当且仅当 f [ y ] < f [ x ] f[y]<f[x] f[y]<f[x] 。
假设对于点 x x x 而言,满足上述条件( f [ y ] < f [ x ] f[y]<f[x] f[y]<f[x])的相邻点 y y y 点一共有 c n t [ x ] cnt[x] cnt[x] 个,则有
f [ x ] = ( d [ x ] − c n t [ x ] ) × f [ x ] + ∑ f [ y ] + c n t [ x ] d [ x ] = ∑ f [ y ] + d [ x ] c n t [ x ] \begin{aligned} \displaystyle f[x]&= \frac{ (d[x]-cnt[x]) \times f[x] + \sum f[y]+cnt[x]}{d[x]} &\\& \displaystyle= \frac{\sum f[y] + d[x] }{cnt[x]} \end{aligned} f[x]=d[x](d[x]−cnt[x])×f[x]+∑f[y]+cnt[x]=cnt[x]∑f[y]+d[x]
显然 ∑ f [ y ] \sum f[y] ∑f[y] 可以直接把所有小于 f [ x ] f[x] f[x] 的都加起来,可以用最短路去实现,因为我们每次从队里拿出来的一定是最小的 f [ y ] f[y] f[y],那么所有与他相邻的点都可以被他更新,我们就可以对于每一个 x x x 能到达的点 y y y ,求和,即 s u m [ y ] = ∑ f [ x ] sum[y]=\sum f[x] sum[y]=∑f[x]。然后对于每一个点 y y y ,直接根据公式更新 f [ y ] f[y] f[y] 即可。
更新的时候,是从 n n n 出发,走到 1 1 1 ,即每次从 x x x 走到 y y y ,那么公式中的 x x x 就是 y y y ,也就是用所有 的 x x x 去更新 y y y,每次更新的是 f [ y ] f[y] f[y] 而不是公式里的 f [ x ] f[x] f[x]。
Code
int n, m, t;
int head[N], ver[M], edge[M], tot, nex[N];
double d[N], cnt[N], sum[N];
double dist[N];
bool vis[N];
void add(itn x, int y)
{
ver[tot] = y;
nex[tot] = head[x];
head[x] = tot ++ ;
}
void dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[n] = 0;
priority_queue<PDI, vector<PDI>, greater<PDI> > q;
q.push({0.0, n});
while(q.size()) {
itn x = q.top().second;
q.pop();
if(vis[x]) continue;
vis[x] = 1;
for(int i = head[x]; ~i; i = nex[i]) {
itn y = ver[i];
if(vis[y]) continue;
cnt[y] ++ ;
sum[y] += dist[x];
dist[y] = (d[y] + sum[y]) / cnt[y];
q.push({dist[y], y});
}
}
}
int main()
{
memset(head, -1, sizeof head);
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++ i) {
itn x, y;
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
d[x] ++ ;
d[y] ++ ;
}
dijkstra();
printf("%.8f\n", dist[1]);
return 0;
}
Q. UVA12177 First Knight(期望DP + 高斯消元 + 优化)
Problem
Solution
f
(
x
,
y
)
f(x,y)
f(x,y) 对其他方程进行消元时,最多也只会影响到
f
(
x
,
y
+
1
)
,
f
(
x
−
1
,
1
)
,
f
(
x
−
1
,
y
)
f(x,y+1),f(x−1,1),f(x−1,y)
f(x,y+1),f(x−1,1),f(x−1,y) 这几项的系数,所以我们只需要讲矩阵消成下三角矩阵,并且我们只需要
a
[
1
]
[
1
]
a[1][1]
a[1][1] 的值,可以去掉回带的操作,跑了
1.5
s
1.5s
1.5s
Code
// Problem: UVA12177 First Knight
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/UVA12177
// Memory Limit: 0 MB
// Time Limit: 9000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 57;
const double eps = 1e-12;
int dir[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};
double p[N][N][4];
int n, m, t;
int r;
int get_id(int x, int y)
{
if(x < 1 || y < 1 || x > n || y > m) return r + 5;
return (x - 1) * m + y;
}
struct Matrix
{
int equ, var;//equ -> 等式个数 var -> 未知变量个数
double a[N * N][N * N];
void clear() {
for (int i = 1; i <= equ; ++ i)
for (int j = 1; j <= var + 1; ++ j)
a[i][j] = 0;
}
double guass() {
for (int i = equ - 1, col = var - 1; i >= 1 && col >= 1; -- i, -- col) {
for (int j = i - 1; j >= 1; -- j) {
double tmp = a[j][col] / a[i][col];
if(fabs(tmp) < eps) continue;
for (int k = col; k >= 1; -- k)
a[j][k] -= a[i][k] * tmp;
a[j][var + 1] -= a[i][var + 1] * tmp;
}
}
return a[1][var + 1] / a[1][1];
}
}M;
int main()
{
while (scanf("%d%d", &n, &m) != EOF && n + m) {
for (int k = 0; k < 4; ++ k) {
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= m; ++ j) {
scanf("%lf", &p[i][j][k]);
}
}
}
r = n * m;
M.equ = M.var = n * m;
M.clear();
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= m; ++ j) {
int id = get_id(i, j);
M.a[id][id] = -1;
if (i == n && j == m) continue;
M.a[id][r + 1] = -1;
for (int k = 0; k < 4; ++ k) {
int now_id = get_id(i + dir[k][0], j + dir[k][1]);
M.a[id][now_id] = p[i][j][k];
}
}
}
puts("");*/
printf("%.1f\n", M.guass());
}
return 0;
}
R. CF24D Broken robot(期望DP + 高斯消元)
Weblink
Problem
Solution
Code
S. UVA 10900 So you want to be a 2n-aire?(连续型概率,均匀分布)
Weblink
https://vjudge.net/problem/UVA-10900
Problem
一个娱乐节目中,刚开始玩家初始的金额为1,有n道题目,答对每一道题目的概率在t到1之间均匀分布,每答一道题,答对的话奖金翻倍,答错的话结束游戏,一分也拿不到。每次听到问题后可以选择答题与不答题,不答题则游戏结束。求玩家在最优策略下能获得的奖金的期望值。
Solution
草稿纸凑合看吧()
Code
T. CF1139D Steps to One(期望,莫比乌斯反演,杜教筛)
Weblink
Problem
Solution
数据较大,要用快速乘不然会爆 long long
Code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 21544400 + 7;
#define minus(x, y) (1ll * x - y < 0 ? 1ll * x - y + mod : 1ll * x - y)
#define plus(x, y) (1ll * x + y >= mod ? 1ll * x + y - mod : 1ll * x + y)
ll mod;
int mu[N];
ll smu[N];
bool vis[N];
unordered_map <ll, ll> sum_mu;
int primes[N], cnt;
ll n, m;
ll mul(ll a, ll b) {
if (mod <= 1000000000) return a * b % mod;
else if (mod <= 1000000000000ll) return (((a * (b >> 20) % mod) << 20) + (a * (b & ((1 << 20) - 1)))) % mod;
else {
ll d = (ll)floor(a * (long double)b / mod + 0.5);
ll res = (a * b - d * mod) % mod;
if (res < 0) res += mod;
return res;
}
}
ll qpow(ll a, ll b)
{
if(mod == 1) return 0;
ll res = 1;
a = a % mod;
while(b) {
if(b & 1) res = mul(res, a);
a = mul(a, a);
b >>= 1;
}
return res;
}
ll inv(ll x)
{
return qpow(x, mod - 2);
}
void init(int n)
{
mu[1] = 1, vis[1] = 1;
for(int i = 2; i <= n; ++ i) {
if(vis[i] == 0) {
primes[ ++ cnt] = i;
mu[i] = -1;
}
for(int j = 1; j <= cnt && i * primes[j] <= n; ++ j) {
vis[i * primes[j]] = 1;
if(i % primes[j] == 0) {
mu[i * primes[j]] = 0;
break;
}
mu[i * primes[j]] -= mu[i];
}
}
for(int i = 1; i <= n; ++ i)
smu[i] = plus(smu[i - 1], mu[i]);
return ;
}
inline ll g_sum(ll x)
{
return x;
}
inline ll get_sum_mu(ll x)
{
if(x <= N - 7) return smu[x];
if(sum_mu.find(x) != sum_mu.end()) return sum_mu[x];
ll ans = 1;
for(ll l = 2, r; l <= x; l = r + 1) {
r = x / (x / l);
ll tmp = mul((r - l + 1), get_sum_mu(x / l));
ans = minus(ans, tmp);
}
ans = (ans % mod + mod) % mod;
sum_mu[x] = ans / g_sum(1ll);
return ans / g_sum(1ll);
}
void solve(ll m)
{
ll ans = 1;
for(ll l = 1, r; l <= m; l = r + 1) {
r = m / (m / l);
ll tmp = mul((get_sum_mu(r) - get_sum_mu(l - 1)), mul(m / l, inv(m - m / l)));
ans = minus(ans, tmp);
}
ans = (ans + mod) % mod;
printf("%lld\n", ans);
return ;
}
signed main()
{
scanf("%lld%lld", &m, &mod);
if(m == 1) return printf("1\n"), 0;
init(N - 7);
solve(m);
return 0;
}
U. LightOJ 1274 Beating the Dataset(期望,贡献和思想)
Weblink
https://vjudge.net/problem/LightOJ-1274
Problem
给定0和1的个数,这些0、1产生的任何一个多重集排列都是可能的,求所有可能的n位01排列中第一位为0的位或者与排列中前面一位不同的位的期望个数。
Solution
草稿纸凑合看吧()
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 7;
int t, kcase;
int n, m, x, y, s;
int main()
{
scanf("%d", &t);
while(t -- ) {
scanf("%d%d", &n, &s);
x = s - 2 * n;
y = 3 * n - s;
printf("Case %d: %.10f\n", ++ kcase, (2.0 * x * y + y) / n);
}
}
V. 牛客练习赛82 D. Mocha 的数据包
Weblink
https://ac.nowcoder.com/acm/contest/11172/D
Problem
Solution
大概是这么个意思吧
Code
W. HDU 4579 Random Walk(期望 + 高斯消元)
X. HDU 6829 Borrow
Y. HDU 6052 To my boyfriend(贡献和)
Z. HDU 4870 Rating
α. LightOJ 1151 Snakes and Ladders
β. P4457 [BJOI2018]治疗之雨(期望 + 高斯消元)
γ. P3600 随机数生成器
%**Weblink****Problem****Solution****Code**
%https://blog.csdn.net/consciousman/article/details/73730834?utm_source=app&app_version=4.5.8
%https://www.luogu.com.cn/blog/ShineEternal/mathematical-expectation
%https://blog.csdn.net/hollyidyllic/article/details/108060378?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242
%R. LightOJ 1274 Beating the Dataset
%Q. LightOJ 1265 Island of Survival
%S. LightOJ 1284 Lights inside 3D Grid
%LightOJ – 1342 Aladdin and the Magical Sticks
%https://blog.csdn.net/u011699990/article/details/38115811?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control