期望问题+ybt题解
算法理解
对于随机变量 \(X\),有 \(n\) 个可能的取值,取值为 \(x_i\) 有 \(P(x_i)\) 的概率,则它的数学期望则为 \(E(X)=\sum_{i=1}^nx_iP(x_i)\)
性质
其中期望的线性限制最重要,它可以将两个完全独立的期望拆分开来单独计算,详见例题
T1:
首先我们观察有 \(n\) 道题,我们根据期望的线性限制把每道题分开来算期望,最后加一下和即可,即是 \(E(ans)=\sum_{i=1}^nE(ans_i)\)
就是对于一道题,答对的概率答对的题数(1)+答错的概率答错的题数(0),就是这道题的 \(E(ans_i)\),然后就可以求解了
分讨:
T2:
有些巧妙的题
我们首先可以想到把一大长串的贡献拆到每一个字符上进行统计(期望的线性限制),考虑对于一串a个连续的o,后面再加上一个o会产生 \(2a+1\) 的贡献,也就是说,我们只需要知道到 \(a_i\) 这一位时的后缀长度就可以算贡献了
考虑加上期望,根据期望的种种公式,我们可以这样考虑:抛开概率,举个例子,我一张卷子,每道题答对有一个概率,我想知道我这张卷子分数的期望,我最终得出来的期望掺杂概率吗,没有,我只会告诉你一个确切的值,而不是有多少概率考这个分,多少概率考那个分,所以最后我们把期望看成一个具体的有实际意义的值即可
所以我们可以考虑算出 \(a_i\) 后缀长度的期望,根据以上的说明,我们就把这个期望值看成真正的后缀长度即可,所以我们设 \(Elen\) 为后缀的期望,把其看成一个确切的长度计算即可
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n;
double ans,Elen;
char s[N];
int main(){
scanf("%d",&n);
scanf("%s",s+1);
for(int i=1;i<=n;i++){
if(s[i]=='o'){
ans+=Elen*2.0+1;
Elen++;
}
else if(s[i]=='x'){
Elen=0;
}
else{
ans+=(Elen*2.0+1)/2.0;
Elen=(Elen+1)/2.0;
}
}
printf("%.4lf",ans);
}
T3:
切了,嘿嘿
首先我们根据期望的线性限制然后可以对每一条边分别考虑,一条边的期望就是用每一条边的边权乘上到达这条边的概率,最后把所有边的期望加起来就可以了
代码:
#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int N=1e5+5;
int n,m;
int chu[N],ru[N];
double f[N],ans;
vector<pii>b[N];
queue<int>q;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
b[u].push_back({v,w});
chu[u]++;
ru[v]++;
}
f[1]=1;
q.push(1);
while(!q.empty()){
int u=q.front();
q.pop();
for(pii i:b[u]){
int v=i.first,w=i.second;
ans+=(double)w*(f[u]/(double)chu[u]);
f[v]+=f[u]/(double)chu[u];
ru[v]--;
if(!ru[v]){
q.push(v);
}
}
}
printf("%.2lf",ans);
}
T4:
想到了 \(O(n^2)\) 没有想到优化
首先我们可以想到对于每一组 \(a_i\) 对应 \(b_j\) 是可以通过期望的线性限制拆开的,权值即为题目描述,然后概率为 \(1/n\)
按照这个就可以 \(O(n^2)\) 做了
考虑优化
我们发现对于一个固定的 \(a_i\) 只有两部分,一部分为小于 \(a_i\),对答案产生正贡献,另一部分大于 \(a_i\) ,对答案产生负贡献
所以我们可以先将B数组排个序,然后又因为 \((x+y)^2=x^2+y^2+2xy\) 所以我们可以预处理出 \(b_i^2\) 和 \(2*b_i\) 的前缀和,然后二分找到分界点,做到 \(O(logn)\) 查询,可以通过
当然还有一种查询 \(O(1)\) 的做法,但总复杂度因为有排序所以没有更优
就是对于A,B数组都排好序,然后跑双指针找到分界点即可
注意开long long
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e4+5;
int n,ans;
int a[N],b[N],sum[N],sq[N];
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%lld",&b[i]);
}
sort(a+1,a+1+n);
sort(b+1,b+1+n);
for(int i=1;i<=n;i++){
sum[i]=sum[i-1]+b[i];
sq[i]=sq[i-1]+b[i]*b[i];
}
for(int i=1,j=0;i<=n;i++){
while(b[j+1]<=a[i]&&j+1<=n) j++;
ans+=a[i]*a[i]*j+sq[j]-2*a[i]*sum[j];
ans-=a[i]*a[i]*(n-j)+sq[n]-sq[j]-2*a[i]*(sum[n]-sum[j]);
}
printf("%.1lf",(double)ans/n);
}
T5:
题意描述的很不清晰,意思是要你找到一种使小明快乐值最大的方案
首先我们可以用期望把其拆成各个电影的期望和
然后考虑每个电影本身带来的贡献不变为 \(l_i(\frac{x_i}{y_i}-\frac{y_i-x_i}{y_i})\),然后考虑两个电影i,j带来的贡献,先看 i 再看 j 贡献为 \(l_i\frac{x_i}{y_i}\frac{y_j-x_j}{y_j}\),所以肯定是贡献越大越靠前,所以在比较函数中就比较 i,j构成的式子大小即可
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5,mod=1004535809;
int n,sum,ans;
int lg[60];
struct movie{
int l,x,y;
}m[N];
bool cmp(movie a,movie b){
return a.l*a.x*(b.y-b.x)>b.l*b.x*(a.y-a.x);
}
int quickpow(int x,int k){
lg[0]=x;
for(int i=1;i<=36;i++){
lg[i]=lg[i-1]*lg[i-1]%mod;
}
int res=1;
for(int i=0;i<=36;i++){
if(!((k>>i)&1ll)) continue;
res=res*lg[i]%mod;
}
return res;
}
int inv(int x){
return quickpow(x,mod-2);
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld",&m[i].l,&m[i].x,&m[i].y);
}
sort(m+1,m+1+n,cmp);
for(int i=1;i<=n;i++){
ans+=m[i].l*(2*m[i].x*inv(m[i].y)%mod-1+mod%mod)%mod;
ans+=sum*(m[i].y-m[i].x)%mod*inv(m[i].y)%mod;
ans%=mod;
ans%=mod;
sum+=m[i].l*m[i].x%mod*inv(m[i].y)%mod;
sum%=mod;
}
printf("%lld",ans);
}
T6:
没有研究明白,原题
gyy的期望补充
P4550
首先我们设 \(f[i],g[i]\) 表示已经买到了i个邮票,取完所有邮票的期望次数/钱数
这个问题不好考虑,所以我们将期望拆成每一次的决策
有 \(\frac{i}{n}\) 的概率取到相同的,有 \(\frac{n-i}{n}\) 的概率取到不同的,然后每一次必须多一次/多一次的钱
根据这些考虑转移
然后根据边界条件 \(f[n]=g[n]=0\) 逆推求得 \(g[0]\)
P8774
首先和上道题一样,因为爬着爬着可能掉回去,所以考虑逆推,设爬到树顶的时间的期望,然后是因为逆推我们算爬到树顶的时间才可以模拟出掉下去还得重新爬的过程
然后推导出式子
考虑这个式子有些特别,因为它不能递推
所以尽量向两个极端靠拢
把式子带入计算,发现一些规律,多手模几项就可以发现规律
然后直接预处理计算即可