概率与期望


本文最后更新于:2017年10月20日 晚上

公式

概率

$P(A)$代表A事件发生的概率,$Ω$为全集。

一、

$1-P(A) = P(Ω-A)$

二、

$2. P(A_1∪A_2)=P(A_1)+P(A_2)-P(A_1∩A_2)$
推论:若$A_1∩A_2 = ∅$,则$P(A_1∪A_2)=P(A_1)+P(A_2)$

三、AB同时发生的概率 = B发生的概率*在B发生下A发生的概率

$P(AB) = P(B)\times P(A|B) = P(A)\times P(B|A)$

期望

随机变量X:是定义在概率空间上的一个函数 $X(w_i) -> k$
期望:定义在一个随机变量X上,写作:$E[X] = \sum P(w_i)\times X(w_i) $
期望是一种加权平均数

一、期望的可加性

$E[X+Y] = E[X] + E[Y]$

Problems

1.[bzoj 2134] 单选错位

Description

gx和lc去参加noip初赛,其中有一种题型叫单项选择题,顾名思义,只有一个选项是正确答案。
试卷上共有n道单选题,第i道单选题有$a_i$个选项,这$a_i$个选项编号是$1,2……,a_i$,每个选项成为正确答案的概率都是相等的。lc采取的策略是每道题目随机写上$1-a_i$的某个数作为答案选项,他用不了多少时间就能期望做对$\sum_{i=1}^n \frac{1}{a_i}$道题目。gx则是认认真真地做完了这n道题目,可是等他做完的时候时间也所剩无几了,于是他匆忙地把答案抄到答题纸上,没想到抄错位了:
第i道题目的答案抄到了答题纸上的第i+1道题目的位置上,特别地,第n道题目的答案抄到了第1道题目的位置上。现在gx己经走出考场没法改了,不过他还是想知道自己期望能做对几道题目,这样他就知道会不会被lc鄙视了。
我们假设gx没有做错任何题目,只是答案抄错位置了。

Input

n很大,为了避免读入耗时太多,输入文件只有5个整数参数$n, A, B, C, a_1$,由上交的程序产生数列a。
下面给出pascal/C/C++的读入语句和产生序列的语句(默认从标准输入读入):
// for pascal

readln(n,A,B,C,q[1]);
for i:=2 to n do q[i] := (int64(q[i-1]) * A + B) mod 100000001;
for i:=1 to n do q[i] := q[i] mod C + 1;

// for C/C++
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;

选手可以通过以上的程序语句得到n和数列a(a的元素类型是32位整数),n和a的含义见题目描述。

Output

输出一个实数,表示gx期望做对的题目个数,保留三位小数。

Sample Input

3 2 0 4 1

Sample Output

1.167

Explanation

a[] = {2,3,1}

正确答案 gx的答案 做对题目 出现概率
{1,1,1} {1,1,1} 3 1/6
{1,2,1} {1,1,2} 1 1/6
{1,3,1} {1,1,3} 1 1/6
{2,1,1} {1,2,1} 1 1/6
{2,2,1} {1,2,2} 1 1/6
{2,3,1} {1,2,3} 0 1/6

共有6种情况,每种情况出现的概率是1/6,gx期望做对(3+1+1+1+1+0)/6 = 7/6题。(相比之下,lc随机就能期望做对11/6题)

Hint

对于100%的数据 $2≤n≤10^7, 0≤A,B,C,a_1≤10^8$

思路

每道题的期望是独立的,当$a_i > a_{i+1}$时,有 $\frac{a_{i+1}}{a_i}$ 的概率选中$1,2……,a_{i+1}$中的一个,这一个正确的概率为$\frac{1}{a_{i+1}}$,选对的概率为$\frac{1}{a_i}$,每道题贡献为1, 每道题期望做对的题目个数为$\frac{1}{a_i}$,当$a_i < a_{i+1}$时同理可得为$\frac{1}{a_{i+1}}$,所以每道题期望做对的题目个数为$\frac{1}{max(a_i,a_{i+1})}$。

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#define mem(a,b) memset(a,b,sizeof(a))
#define fr(x) freopen(#x ".in","r",stdin);freopen(#x ".out","w",stdout)
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=1e7+10,mod=100000001;
int a[maxn]={0},n,A,B,C;
int read();
int main()
{
//	fr();
	scanf("%d%d%d%d%d",&n,&A,&B,&C,&a[1]);
	for(int i=2;i<=n;i++) a[i]=(1ll*a[i-1]*A+B)%mod;
	for(int i=1;i<=n;i++) a[i]=a[i]%C+1;
	a[0]=a[n];
	db ans=0.0;
	for(int i=1;i<=n;i++) ans+=1.0/max(a[i],a[i-1]);
	printf("%.3f\n",ans);
//	fprintf(stderr,"%.3f s\n",clock()*1.0/CLOCKS_PER_SEC);
	return 0;
}
int read()
{
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x*f;
}

2.[bzoj 1419] Red is good

题目描述

桌面上有$R$张红牌和$B$张黑牌,随机打乱顺序后放在桌面上,开始一张一张地翻牌,翻到红牌得到$1$美元,黑牌则付出$1$美元。可以随时停止翻牌,在最优策略下平均能得到多少钱。

输入格式

一行输入两个数$R$,$B$,其值在$0$到$5000$之间

输出格式

在最优策略下平均能得到多少钱。

样例输入

5 1

样例输出

4.166666

提示

输出答案时,小数点后第六位后的全部去掉,不要四舍五入.

思路

考虑dp,$f[i][j]$表示有i张红牌和j张黑牌,在最优策略下平均能得到多少钱,则$f[i][j]=max(0,\frac{i}{i+j}\times (f[i-1][j]+1)+\frac{j}{i+j}\times (f[i][j-1]-1))$,如期望小于0,不取它一定更优,由于每层状态只与上一层有关,用滚动数组优化。

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#define mem(a,b) memset(a,b,sizeof(a))
#define fr(x) freopen(#x ".in","r",stdin);freopen(#x ".out","w",stdout)
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=5e3+10;
db f[2][maxn];
int R,B;
int read();
int main()
{
//	fr();
	R=read(),B=read();
	bool d=0;
	for(int i=1;i<=R;i++)
	{
		f[d][0]=i;
		for(int j=1;j<=B;j++)
		{
			f[d][j]=max(0.0,i*1.0/(i+j)*(f[d^1][j]+1)+j*1.0/(i+j)*((f[d][j-1])-1));
		}
		d^=1;
	}
	printf("%.6f\n",f[d^1][B]-5e-7);	
//	fprintf(stderr,"%.3f s\n",clock()*1.0/CLOCKS_PER_SEC);
	return 0;
}
int read()
{
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x*f;
}

3. [bzoj 2438] [中山市选2011]杀人游戏

Description

一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,查出谁是杀手。
警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀手, 杀手将会把警察干掉。
现在警察掌握了每一个人认识谁。
每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。
问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?

Input

第一行有两个整数 N,M。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x,例如胡锦涛同志) 。

Output

仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。

Sample Input

5 4
1 2
1 3
1 4
1 5

Sample Output

0.800000

HINT

警察只需要查证 1。假如1是杀手,警察就会被杀。假如 1不是杀手,他会告诉警察 2,3,4,5 谁是杀手。而 1 是杀手的概率是 0.2,所以能知道谁是杀手但没被杀的概率是0.8。
对于100%的数据有 $1≤N ≤ 10 0000,0≤M ≤ 30 0000$。

思路

要使知道谁是杀手的概率最大,需要少问,对于环上的点,只需问一次,所以先Tarjan缩点,入度为0的scc是一定会问的,但有一种特殊情况,如存在一个scc的size为1,入度为0且出度为0或指向的点有其它入边,则知道x-1个就行啦,问的次数需减一。

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#define mem(a,b) memset(a,b,sizeof(a))
#define fr(x) freopen(#x ".in","r",stdin);freopen(#x ".out","w",stdout)
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=1e5+10;
typedef int INT[maxn];
struct edge
{
	int to,next,from;
	edge(){}
	edge(int to,int next,int from):to(to),next(next),from(from){}
}e[(maxn<<1)+maxn];
INT pre,low,dfn,stack,sccno,in,sz;
bool ins[maxn];
int tot=0,top=0,n,m,sccCnt=0,dfsClock=0;
int read();
void findScc();
void makeScc();
void add(int,int);
void dfs(int);
int main()
{
//	fr();
	mem(pre,-1);
	n=read(),m=read();
	for(int i=1,f,t;i<=m;i++)
	{
		f=read(),t=read();
		add(f,t);
	}
	findScc();
	makeScc();
//	fprintf(stderr,"%.3f s\n",clock()*1.0/CLOCKS_PER_SEC);
	return 0;
}
void add(int from,int to)
{
	e[++tot]=edge(to,pre[from],from);pre[from]=tot;
}
void makeScc()
{
	int ans=0;
	for(int i=1;i<=tot;i++)
	{
		int ito=e[i].to,ifr=e[i].from;
		if(sccno[ifr]^sccno[ito]) in[sccno[ito]]++;
	}
	for(int i=1;i<=sccCnt;i++) if(!in[i]) ans++;
	for(int i=1;i<=n;i++)
	{
		if(sz[sccno[i]]==1&&!in[sccno[i]])
		{
			bool flag=true;
			for(int j=pre[i];~j;j=e[j].next)
			{
				int jto=e[j].to;
				if(in[sccno[jto]]<=1){flag=false;break;}
			}
			if(flag){ans--;break;}
		}
	}
	db Ans=1.0*(n-ans)/n;
	printf("%.6f\n",Ans);
}
void findScc()
{
	dfsClock=sccCnt=0;
	mem(low,0),mem(sccno,0),mem(dfn,0),mem(ins,0),mem(stack,0),mem(sz,0),top=0;
	for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i);
}
void dfs(int s)
{
	dfn[s]=low[s]=++dfsClock;
	ins[stack[++top]=s]=true;
	for(int i=pre[s];~i;i=e[i].next)
	{
		int ito=e[i].to;
		if(!dfn[ito]) dfs(ito),low[s]=min(low[s],low[ito]);
		else if(ins[ito]) low[s]=min(dfn[ito],low[s]);
	}
	if(low[s]^dfn[s]) return;
	sccCnt++;
	while(true)
	{
		int x=stack[top--];
		ins[x]=false;
		sccno[x]=sccCnt;
		sz[sccCnt]++;
		if(x==s) break;
	}
}
int read()
{
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x*f;
}

4. [tyvj 1952] Easy

描述

某一天WJMZBMR在打osu~~~但是他太弱逼了,有些地方完全靠运气:(
我们来简化一下这个游戏的规则
有n次点击要做,成功了就是o,失败了就是x,分数是按comb计算的,连续a个comb就有aa分,comb就是极大的连续o。
比如ooxxxxooooxxx,分数就是2
2+4*4=4+16=20。
Sevenkplus闲的慌就看他打了一盘,有些地方跟运气无关要么是o要么是x,有些地方o或者x各有50%的可能性,用?号来表示。
比如oo?xx就是一个可能的输入。
那么WJMZBMR这场osu的期望得分是多少呢?
比如oo?xx的话,?是o的话就是oooxx => 9,是x的话就是ooxxx => 4
期望自然就是(4+9)/2 =6.5了

输入格式

第一行一个整数n,表示点击的个数
接下来一个字符串,每个字符都是ox?中的一个

输出格式

一行一个浮点数表示答案
四舍五入到小数点后4位
如果害怕精度跪建议用long double或者extended

测试样例1

输入

4
????

输出

4.1250

备注

osu很好玩的哦
WJMZBMR技术还行(雾),x基本上很少呢

思路

$f[i]$表示前i个的期望,$g[i]$表示最后comb的期望长度。

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#define mem(a,b) memset(a,b,sizeof(a))
#define fr(x) freopen(#x ".in","r",stdin);freopen(#x ".out","w",stdout)
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=5e5+10;
int n;
char s[maxn];
db f[maxn],g[maxn];
int read();
int main()
{
//	fr();
	n=read();
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)
	{
		if(s[i]=='x') f[i]=f[i-1],g[i]=0;
		else if(s[i]=='o') f[i]=f[i-1]+g[i-1]*2+1,g[i]=g[i-1]+1;
		else f[i]=f[i-1]+g[i-1]+0.5,g[i]=(g[i-1]+1)/2;
	}
	printf("%.4f\n",f[n]);
//	fprintf(stderr,"%.3f s\n",clock()*1.0/CLOCKS_PER_SEC);
	return 0;
}
int read()
{
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x*f;
}

文章作者: SpaceSkyNet
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 SpaceSkyNet !
  目录
评论