一些期望题

P1297 [国家集训队]单选错位

题链
我们可以发现n是2e7 那显然是线性的
我们直接对每一位考虑
1.要是a[i]=a[i-1] 那么我们正确的概率就是1/a[i]
2.要是a[i]>a[i-1] 那么我们必须选到a[i-1]的那些个 1/a[i-1]那么我们a[i]的选择就有a[i-1]个概率是1/a[i] = 1/a[i]
3.a[i]<a[i-1]同理 我们要选到1/a[i]个 左边有a[i]个概率是1/a[i-1]
所以我们的答案就是/sigma 1/max(a[i],a[i-1])

int n,A,B,C,a[N];
void solve(){
    a[n+1]=a[1];
    double ans=0;
    for(int i=2;i<=n+1;i++){
        ans+=1.0/max(a[i],a[i-1]);
    }
    printf("%.3lf",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;
}

P5104 红包发红包

当然也可以是直接乱搞结论
我们肯定知道每次散出去的期望是w/2
下一次就是w/2/2
这样就搞出来了w/(2^k)

void solve(){
    cin>>w>>n>>k;
    cout<<w*inv(qmi(2,k,mod))%mod;
}

2020 南京 F.Fireworks

同样是乱搞
我们知道制作k次然后成功的概率是
p=成功概率
q=1-p=失败概率
1-q^k 只有全部失败才算失败 否则都算成功 这个就是成功概率
成功概率的倒数就是期望的轮数
我们要求的是期望的最小值
我们制作k个要的时间是(kn+m)
所以E=(k
n+m)/(1-q^k)
暴力枚举肯定会T
我们随便猜个凹函数三分即可
//upt
我们也可以公式化 dp的来看看

显然我们这列就要搞个dp
dp[i]表示该状态下走到结束状态的期望步数
那么i就只有0/1两种状态 分别表示成功失败
这样我们dp[1]=0;
答案就是要dp[0]
dp[0]=((kn+m)[时间]+dp[1])(1-qk)[概率]+(qk)((kn+m)[时间]+dp[0])
然后移项就可以搞出
dp[0]=(k*n+m)/(1-q^k)

double f(int k){
    return (k*n+m)*1.0/(1.0-pow(1-p,k));
}
void solve(){
    cin>>n>>m;
    cin>>p;
    p/=10000;
    int l=1,r=2e5;
    double lans,rans;
    while(l < r) {
        int lmid = l + (r - l) / 3;
        int rmid = r - (r - l) / 3;
        lans = f(lmid),rans = f(rmid);
        if(lans <= rans) r = rmid - 1;
        else l = lmid + 1;
    }
    printf("%.8lf\n",min(lans,rans));
}

总结这样不用dp的题 基本上都是与前面做的操作无关的
然后就会有一些类似于计数的概率题出现

P6154 游走

问的是每条路径长度的期望
我们可以直接将走过的长度记录下来
再将路径数量记录下来
然后相除就是期望了

vector<int>g[N];
void dp(int u){
    if(f[u])return;
    b[u]=1;
    for(auto v:g[u]){
        dp(v);
        (f[u]+=f[v]+b[v])%=mod;
        (b[u]+=b[v])%=mod;
    }
}
void solve(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v;cin>>u>>v;
        g[u].push_back(v);
    }
    for(int i=1;i<=n;i++)if(!f[i])dp(i);
    int ans=0,s=0;
    for(int i=1;i<=n;i++)(ans+=f[i])%=mod;
    for(int i=1;i<=n;i++)(s+=b[i])%=mod;
    cout<<ans*inv(s)%mod<<endl;
}

P3802 小魔女帕琪

还有一些和组合数相关了
我们考虑在那些地方可以放置这7个魔法
然后我们让这连续的七个位置独立出来
这样就相当于上面的方案数/总和
但是这里的总和不好算出来
我们考虑直接搞出期望
我们知道有N-6个地方可以放置
而且这7个位置可以全排列就是7!
最后我们把这七个的概率搞出来
利用线性性E[CX]=C*E[x]就可以了

void solve(){
    vector<int>a(7);
    double s=1;
    int sum=0;
    for(int i=0;i<7;i++)cin>>a[i],sum+=a[i],s*=a[i]*(i+1);
    for(int i=sum;i>=sum-5;i--)s/=i;
    printf("%.3lf",s);
}

P1654 OSU!

这个就是春春线性性应用了
知道E[(x+1)3]-E[x3]=E[3x^2]+E[3x]+1
我们维护这样一个E[x^2]和E[x]即可

void solve(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>p[i];
        x1[i]=(x1[i-1]+1)*p[i];
        x2[i]=(x2[i-1]+2*x1[i-1]+1)*p[i];
        x3[i]=x3[i-1]+(3*x2[i-1]+3*x1[i-1]+1)*p[i];
    }
    printf("%.1lf",x3[n]);
}

UVA1639 糖果 Candy

还有一些精度优化
对于这题我们显然常规的终点要吗是一个盒子为空是终点
或者第二个盒子为空是终点
我们第一个盒子为空:
然后第二个盒子只吃了i个
相当于又n+1个间隙 放i个二 就是Cn+1 i 再乘上p^(n+1)最后发现了没有了还要再打开一次
在乘上q^i次方
但是这样Cn+i*i 显然是会爆ll的
我们db和ll没法好计算
我们就可以对这个算式整体取ln
就变成了

然后预处理阶乘的ln即可

void solve(){
    for(int i=1;i<=400000;++i) {
        F[i] = F[i-1] + log(i);
    }
    int cnt = 0;
    while(scanf("%d%lf",&n,&p)!=EOF) {
        double EX = 0.0;
        q = log(1-p);
        p = log(p);
        for(int i=0;i<=n;++i) {
            w_1 = F[2*n-i] - F[n] - F[n-i] + (n+1) * p + (n-i) * q;
            w_2 = F[2*n-i] - F[n] - F[n-i] + (n+1) * q + (n-i) * p;
            EX += i * ( exp(w_1) + exp(w_2) );
        }
        printf("Case %d: %.6lf\n",++cnt,EX);
    }
}

C. Bubble Strike

题链
显然我们可以分类讨论
也就只有4种情况
3个会 2个会 1个会 0个会
然后分别计算出概率 我们假设会的图有x个
3个会的 肯定最终结果也是选到会的 x/n(x-1)/(n-1)(x-2)/(n-2)
2个会的 那显然我把不会的那个筛掉 那么我还是肯定会的 x/n(x-1)/(n-1)(n-x)/n-2但是这里要3是因为我们顺序可以交换
1个会的 我只能筛掉一个 还有就是对半开的概率要吗选到不会的要吗就是会的
x/n
(n-x)/(n-1)(n-x-1)/(n-2)这里也可以交换顺序所以3
没有会的不管怎么筛都是不会的 所以我们就不计算概率进去了
然后会发现这是个三次函数我们求0点 发现n只有1e3 直接暴力枚举即可

int f(int x) {
    db res=0;
    if(x>=2)res+=x*(x-1)*(x-2);
    if(x>=1)res+=3*x*(x-1)*(n-x);
    if(n>=x+1)res+=(3*x*(n-x)*(n-x-1))*1.0/2;
    return res>=m*n*(n-1)*(n-2);
}
void solve(){
    cin>>n>>m;
    for(int i=0;i<=n;i++){
        if(f(i)){
            cout<<i<<endl;
            return;
        }
    }
}

D. Ilya and Escalator

题链
显然范围2000
我们考虑dp
dp[i][j]表示第i秒有j人在里面的概率 然后最后我们t秒直接用概率人数就是期望了
初始化 f[0][0]=1
考虑转移
显然我们
dp[i][j]=dp[i-1][j-1]
p+dp[i-1][j]*(1-p)
但是这个转移要是j0时 我们不能进行前面一个转移
要是j
n时 我们后面的那个转移就是百分之百的

db dp[2010][2010];
int n,t;db p;
void solve(){
    dp[0][0]=1;
    cin>>n>>p>>t;
    for(int i=1;i<=t;i++){
        for(int j=0;j<=max(i,n);j++){
            if(j==0)dp[i][j]=dp[i-1][j]*(1.0-p);
            else if(j==n)dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*p;
            else dp[i][j]=dp[i-1][j-1]*p+dp[i-1][j]*(1.0-p);
        }
    }
    db ans=0;
    for(int j=0;j<=n;j++)ans+=dp[t][j]*j;
    printf("%.8lf",ans);
}
posted @ 2022-12-05 21:26  ycllz  阅读(31)  评论(0)    收藏  举报