题解 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;
}
原文地址:https://www.cnblogs.com/lhm-/p/13538855.html