相关概念
- 样本点:随机试验中可能的结果。
- 样本空间:随机试验中所有可能情况构成的集合。
- 随机事件:样本空间的子集。(是由样本点构成的集合)
- 随机变量:把样本点映射为实数的函数。
- 离散型(有限个或者是可数的【阿列夫零】)
- 连续型
- 概率的定义:
设样本空间为Ω,对于该样本空间中的任意一个随机事件,都有实值函数 P ( A ) P(A) P(A)使得
P(A)大于等于0
P(Ω) 等于1
对于两个互斥事件(不可能同时发生),两个事件所组成的随机事件概率是两个随机事件分别发生的概率之和。
- 数学期望:
即随机变量的取值以及期望的乘积之和。
**性质:**期望是线性函数
eg. 对于投掷两枚骰子,点数之和的数学期望就相当于是投掷一枚骰子所得点数的期望乘以2.
AcWing216. Rainbow的信号
思路
注意位运算的性质:位运算的一位上的数字不会影响其他位数。
计算可能情况的概率
- 对于长度为1的区间,概率为 1 N 2 1\over{N^2} N21。
- 对于长度大于1的区间,概率为 2 N 2 2\over{N^2} N22。
然后累加所有情况乘以概率即可。
但是如果使用 O ( N 2 ) O(N^2) O(N2)的方法,肯定超时。
这个时候,使用位运算的性质来减少复杂度。
不妨使用last[x]
,x取 0 或者 1 来表示上一个0/1所在的位置。
第一步:扫描所有长度为1的区间,如果是1,那么就累加 1 N 2 {1}\over{N^2} N21
第二步:(采用了数***算来对遍历进行了简化)
- 对于与运算
- 如果这一位是1,那么答案累加 2 k ∗ ( ( r − 1 ) − ( l a s t [ 0 ] + 1 ) + 1 ) ∗ 2 N 2 2^k*{( (r-1) - (last[0]+1) +1)*2}\over{N^2} N22k∗((r−1)−(last[0]+1)+1)∗2
- 对于或运算
- 如果这一位是1,那么答案累加 2 k ∗ ( r − 1 ) ∗ 2 N 2 2^k*{(r-1)*2}\over{N^2} N22k∗(r−1)∗2
- 如果这一位是0,那么答案累加 2 k ∗ ( l a s t [ 1 ] ) ∗ 2 N 2 2^k*{(last[1])*2}\over{N^2} N22k∗(last[1])∗2
- 对于异或运算,有一些复杂
摘抄自算法竞赛进阶指南
代码
#include <bits/stdc++.h>
using namespace std;
#define N 100020
int s[N];
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", s+i);
double ansxor, ansor, ansand;
ansand = ansor = ansxor = 0;
for(int i = 0; i <= 31; i ++)//我在这里先处理or和and
{
for(int j = 1; j <= n; j++)
{
if((s[j] >> i) & 1)
{
ansand += (double)(1<<i)/n/n;
ansor += (double)(1<<i)/n/n;
ansxor += (double)(1<<i)/n/n;
}
}
int last[2] = {
0};
for(int j = 1; j <= n; j++)
{
if((s[j]>>i) & 1)
{
//if(last[0]!=0)//这句话其实并没有必要,因为
/* 1.当j为1的时候,得到的答案是0 2.当j>1的时候,正好复合预期! */
ansand+=(double)(1<<i)*2*(j-last[0]-1)/n/n;
ansor += (double)(1<<i)*(j-1)*2/n/n;
last[1] = j;
}
else
{
ansor += (double)(1<<i)*last[1]*2/n/n;
last[0] = j;
}
}
int c1, c2;
c1 = 0;
c2 = 0;
for(int j = 1; j <= n; j++)
{
if((s[j] >> i)&1)
{
ansxor += (double)(1<<i)*c1*2/n/n;
c1++;
swap(c1, c2);
}
else
{
ansxor += (double)(1<<i)*(c2)*2/n/n;
c1++;
}
}
}
printf("%.3lf %.3lf %.3lf", ansxor, ansand, ansor);
return 0;
}
AcWing217. 绿豆蛙的归宿
思路
当提及到有向无环图,我就立即想起了拓扑序。
拓扑序并不一定是要把序列存起来,然后再遍历一遍。而是在过程中就可以进行遍历。
代码
#include <bits/stdc++.h>
using namespace std;
#define N 200020
int ver[N], edge[N], head[N], nxt[N];
int tot;
int deg[N], out[N];
void add(int x, int y, int w)
{
ver[++tot] = y;
edge[tot] = w;
nxt[tot] = head[x];
head[x] = tot;
}
double dis[N];
stack<int> st;
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(b, a, c);//反过来建图
deg[a]++;
out[a]++;
}
st.push(n);
while(!st.empty())
{
int t = st.top();
st.pop();
for(int i = head[t]; i; i = nxt[i])
{
int xx = ver[i];
dis[xx] += (double)(edge[i]+dis[t])/deg[xx];
out[xx]--;
if(out[xx]==0) st.push(xx);
}
}
printf("%.2lf", dis[1]);
return 0;
}
AcWing218. 扑克牌
思路
这道题目是关于动态规划的,我浅浅地参考一下标程。