YbtOJ 「数学基础」 第6章 期望问题
期望概率dp
\(E(X)=\sum_{i=1}^\inf x_i*p_i\)
期望的性质:
- \(E(c)=c\)
- \(E(cx)=cE(x)\)
- \(E(x+y)=E(x)+E(y)\)
- \(E(xy)=E(x)E(y)\ (xy互相独立)\)
在无穷多次实验中 概率和期望相等
A. 【例题1】单选错位
我们对于相邻两个可以转移的错位单选 选项个数为\(x,y\)
如果\(x>y\) 那么做对这道题的概率\(\frac yx \times \frac 1 y\)
如果\(x=y\) 那么这道题的概率\(\frac 1y\)
如果\(x<y\)那么做对这道题的概率为\(\frac xy \times \frac 1 x\)
所以综合来看 做对每一道题的期望为\(\frac 1 {max(a[i],a[i+1])}\)
需要特判\(i=n\)的情况
#include <bits/stdc++.h>
using namespace std;
#define inl inline
const int N = 1e7 + 5;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , A , B , C , a[N];
double ans;
void init ()
{
scanf("%d%d%d%d%d", &n, &A, &B, &C, a + 1);
for (int i = 2; i <= n; i++)
a[i] = ((long long) a[i - 1] * A + B) % 100000001;
for (int i = 1; i <= n; i++)
a[i] = a[i] % C + 1;
}
signed main ()
{
init();
for ( int i = 1 ; i < n ; i ++ )
ans += 1.0 / max ( a[i] , a[i+1] );
ans += 1.0 / max ( a[n] , a[1] );
cout << fixed << setprecision(3) << ans << endl;
return 0;
}
B. 【例题2】期望分数
[题目大意]
给定一个序列,一些位置未确定(是\(o\)与\(x\)的几率各占50%),对于一个\(ox\)序列,连续\(a\)长度的\(o\)会得到\(a^2\)的收益,请问最终得到的序列的期望收益是多少?
[输入格式]
第一行一个整数 \(n(n\le3×10^5)\),表示点击的个数
接下来一个字符串,每个字符都是 o
,x
,?
中的一个
[输出格式]
一行一个整数,表示最终得到的序列的期望收益
[算法分析]
设\(len(i)\)表示以\(i\)为结尾的极大连续\(o\)的长度的期望,\(f(i)\)表示以\(i\)为结尾的期望得分 第i个字符有三种情况:
- \(o\) 对答案的贡献为\(f_i=f_{i-1}+(len[i-1]+1)^2-len[i-1]^2=f_{i-1}+len[i-1]*2+1\) 且\(len[i]=len[i-1]+1\)
- \(x\) 对答案的贡献为\(f_i=f_{i-1}\)且\(len[i]=0\)
- \(?\) 对答案的贡献为\(f_i=f_{i-1}+\frac{x(i-1)*2+1}2+\frac 0 2\) 且\(len[i]=\frac { len[i-1]+1} 2+\frac 0 2\) 也就是将前两个贡献合并起来 分别对于\(x\)和\(o\)的情况算两种值
[代码实现]
#include <bits/stdc++.h>
using namespace std;
#define inl inline
#define int long long
const int N = 1e6 + 5;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n;
double f[N] , len[N];
string s;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read();
cin >> s , s = ' ' + s;
for ( int i = 1 ; i <= n ; i ++ )
{
if ( s[i] == 'o' ) f[i] = f[i-1] + len[i-1] * 2 + 1 , len[i] = len[i-1] + 1;
if ( s[i] == 'x' ) f[i] = f[i-1] , len[i] = 0;
if ( s[i] == '?' ) f[i] = f[i-1] + len[i-1] + 0.5 , len[i] = ( len[i-1] + 1 ) / 2;
}
cout << fixed << setprecision(4) << f[n] << endl;
return 0;
}
C. 【例题3】路径长度
[题目大意]
DAG 给出张个点m条边的有向无环图,起点为1,终点为n 保证连通
绿豆蛙从起点出发,走向终点。到达每一个顶点时,如果该节点有条出边,绿蛙可以选择任意一条边离开该点,并且走向每条边的概率为\(\frac 1 k\)。
现在绿豆蛙想知道,从起点走到终点的所经过的路径总长度期望是多少?
[输入格式]
输入的第一行是两个整数,分别代表图的点数和边数
第2到第(m+1)行,每行有三个整数\(u,v,w\),代表存在一条从指向长度为的有向边。
[输出格式]
输出一行一个实数代表答案,四舍五入保留两位小数。
[算法分析]
反图的原因:对于每一个点 它只可能从它的入边转移 如果正推的话 需要将出边乘上概率再除上边的终点的入度 较为麻烦
模板题 建立反图进行拓扑序dp 每个点记录出边个数(原图) 和拓扑排序需要的入度(新图)
我们设置\(f[i]\)表示\(i\)点到终点\(n\)的期望路径总长度 那么显然每个终点的初始值为\(0\) 对于一条有向边\((u,v)\) 有\(f[u]=\frac{\sum f[y]+e[i].w}{num[u]}\)其中的\(num[u]\)为\(u\)点的出边个数
拓扑同时进行\(dp\)即可
[代码实现]
#include <bits/stdc++.h>
using namespace std;
#define inl inline
#define int long long
const int N = 1e6 + 5;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m , sta[10000000] , tp , num[N] , rd[N];
double f[N];
int head[N] , cnt;
struct node { int to , nxt , w; } e[N];
void add ( int u , int v , int w ) { e[++cnt] = { v , head[u] , w }; head[u] = cnt; }
void tuopu()
{
for ( int i = 1 ; i <= n ; i ++ ) if ( !rd[i] ) sta[++tp] = i;
while ( tp )
{
int u = sta[tp--];
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
f[v] += ( f[u] + e[i].w ) / num[v];
if ( !--rd[v]) sta[++tp] = v;
}
}
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
for ( int i = 1 , u , v , w ; i <= m ; i ++ ) u = read(), v = read() , w = read() , add ( v , u , w ) , rd[u] ++ , num[u] ++;
tuopu();
cout << fixed << setprecision(2) << f[1] << endl;
return 0;
}