YbtOJ 「数学基础」 第6章 期望问题

期望概率dp

\(E(X)=\sum_{i=1}^\inf x_i*p_i\)

期望的性质:

  1. \(E(c)=c\)
  2. \(E(cx)=cE(x)\)
  3. \(E(x+y)=E(x)+E(y)\)
  4. \(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)\),表示点击的个数

接下来一个字符串,每个字符都是 ox? 中的一个

[输出格式]

一行一个整数,表示最终得到的序列的期望收益

[算法分析]

\(len(i)\)表示以\(i\)为结尾的极大连续\(o\)的长度的期望,\(f(i)\)表示以\(i\)为结尾的期望得分 第i个字符有三种情况:

  1. \(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\)
  2. \(x\) 对答案的贡献为\(f_i=f_{i-1}\)\(len[i]=0\)
  3. \(?\) 对答案的贡献为\(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;
}
posted @ 2023-06-30 07:51  Echo_Long  阅读(72)  评论(0编辑  收藏  举报