• 题解 CF1067D 【Computer Game】


    先考虑贪心,发现当升级后一定是一直选取 (bp) 最大,即期望收益最大的游戏,而升级前的选取要考虑升级的概率和当前选取的收益,所以考虑 (DP) 来解决升级前的选取。

    (f_t) 为还剩 (t) 秒且当前未成功升级的最大期望收益,(v)(bp) 最大值,得:

    [large f_t=max_{i=1}^n{ p_i((t-1)v+a_i)+(1-p_i)f_{t-1} } ]

    即为成功后就一直选取最优的选择,未成功就继续 (DP)。将方程转化为另一种形式,得:

    [large f_t=max_{i=1}^n{ p_i((t-1)v-f_{t-1})+p_ia_i }+f_{t-1} ]

    发现转移可以表示为 (kx+b) 的形式,(p_i) 看作为 (k)(p_ia_i) 看作为 (b)。也就是每个转移都可以用一条直线来表示,考虑斜率优化,维护出下凸壳后二分出 (x=(t-1)v-f_{t-1}) 在下凸壳的哪一条直线上即可。但是发现秒数 (t) 很大,不能直接做。

    发现 (tv-f_t) 是单调不降的,假设有 (tv-f_t geqslant (t-1)v-f_{t-1}),得 (f_t-f_{t-1} leqslant v),根据 (v) 的定义,每一次的最优决策所带来的收益不可能超过 (v),因此得证 (tv-f_t) 是单调不降的。

    那么就可以直接扫一遍直线,二分每次连续可行转移的 (t) 的区间,通过矩阵快速幂来优化转移:

    [largeegin{bmatrix} f_t & t & 1 end{bmatrix} imes egin{bmatrix} 1-p_i & 0 & 0 \ p_iv & 1 & 0 \ p_ia_i & 1 & 1 end{bmatrix} = egin{bmatrix} f_{t+1} & t+1 & 1 end{bmatrix} ]

    二分是两个 (log) 的,通过矩阵倍增来代替二分转移就可以做到一个 (log)

    #include<bits/stdc++.h>
    #define maxn 100010
    #define eps 1e-12
    using namespace std;
    typedef long long ll;
    template<typename T> inline void read(T &x)
    {
        x=0;char c=getchar();bool flag=false;
        while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        if(flag)x=-x;
    }
    int n,tot,top;
    ll t,now;
    double v;
    struct matrix
    {
        double a[5][5];
    }f,g[36];
    matrix operator *(const matrix &x,const matrix &y)
    {
        matrix z;
        memset(z.a,0,sizeof(z.a));
        for(int k=1;k<=3;++k)
            for(int i=1;i<=3;++i)
                for(int j=1;j<=3;++j)
                    z.a[i][j]+=x.a[i][k]*y.a[k][j];
        return z;
    }
    int sgn(double x)
    {
        if(fabs(x)<eps) return 0;
        return x>0?1:-1;
    }
    struct line
    {
        double k,b;
    }l[maxn],st[maxn];
    bool cmp(const line &x,const line &y)
    {
        if(!sgn(x.k-y.k)) return x.b<y.b;
        return x.k<y.k;
    }
    double get(line x,line y)
    {
        return (x.b-y.b)/(y.k-x.k);
    }
    int main()
    {
        read(n),read(t);
        for(int i=1;i<=n;++i)
        {
            double a,b,p;
            scanf("%lf%lf%lf",&a,&b,&p);
            l[i]={p,p*a},v=max(v,b*p);
        }
        sort(l+1,l+n+1,cmp);
        for(int i=1;i<=n;++i)
            if(sgn(l[i].k-l[i+1].k))
                l[++tot]=l[i];
        for(int i=1;i<=tot;++i)
        {
            while(top>1&&sgn(get(st[top],st[top-1])-get(l[i],st[top-1]))>=0) top--;
            st[++top]=l[i];
        }
        f.a[1][3]=1;
        for(int i=1;i<=top;++i)
        {
            double pos=now*v-f.a[1][1];
            while(i<top&&sgn(pos-get(st[i],st[i+1]))>=0) i++;
            if(i<top) pos=get(st[i],st[i+1]);
            g[0].a[1][2]=g[0].a[1][3]=g[0].a[2][3]=0;
            g[0].a[2][2]=g[0].a[3][2]=g[0].a[3][3]=1;
            g[0].a[1][1]=1-st[i].k,g[0].a[2][1]=st[i].k*v,g[0].a[3][1]=st[i].b;
            for(int j=1;j<=34;++j) g[j]=g[j-1]*g[j-1];
            for(int j=34;j>=0;--j)
                if(now+(1ll<<j)<t)
                    if(i==top||sgn(pos-(now+(1ll<<j))*v+(f*g[j]).a[1][1])>=0)
                        f=f*g[j],now+=1ll<<j;
            f=f*g[0],now++;
            if(now==t) break;
        }
        printf("%.10lf",f.a[1][1]);
        return 0;
    }
    
  • 相关阅读:
    Python之sys & os
    1161
    1142
    P1599 货币
    P1547逆转,然后再见
    P1629八
    P1753HackSon的趣味题
    Problem 2233 ~APTX4869
    1269
    1091. Tmutarakan Exams
  • 原文地址:https://www.cnblogs.com/lhm-/p/13538855.html
Copyright © 2020-2023  润新知