题意

有一个长度为 \(n\) 的随机数列 \(\{p_i\}\)。每个 \(p_i\) 独立等概率在 \(1\sim m\) 中选取。

你选数的过程如下:

  • 从 \(i=1\) 开始,每一步只能看到当前 \(p_i\) 。你需要决策是否选择 \(p_i\):若选择,则直接结束;否则继续看 \(p_{i+1}\)。
  • 不能回头选择 \(i\) 之前的数。

最大化选出的数的期望,求出这个最大期望。

\(1\le n\le 10^{9},2\le m\le 10^6\)。

Solution

由于每个数独立,\(i\) 之前的数对 \(i\) 处决策没有影响。不难得到一个 \(O(n)\) 的 DP:

设 \(f_i\) 为有 \(i\) 个数的最大期望,答案即为 \(f_n\)。

显然 \(f_1=(1+m)/2\)。对于一个位置 \(i\):

  • 若 \(p_i\le f_{i+1}\)(即 \(p_i\le \lfloor f_{i+1}\rfloor\))那么不选 \(p_i\)
  • 否则选 \(p_i\) 并退出

综上,设 \(t=\lfloor f_{i-1} \rfloor\),转移方程为:

\[f_i=\frac{1}{m}\left(tf_{i-1}+\frac{(m-t)(m+t+1)}{2}\right) \]

注意到 \(f\) 满足:

  • \(f_i\) 单调递增
  • \(f_i

因此 \(\lfloor f_{i} \rfloor\) 单调不降,当 \(\lfloor f_{i} \rfloor=m-1\) 后不再改变。

我们先暴力 DP 直到 \(f_{i}\ge m-1\),设 \(a\) 为第一个满足此条件的 \(i\)。

此后转移方程变成:

\[f_i=\frac{m-1}{m}f_{i-1}+1\quad (i>a) \]

这是一个一阶线性递推,不动点法得:

\[f_i-m=\frac{m-1}{m}\left(f_{i-1}-m\right)\quad (i>a) \]

因此 \(f_n=(f_a-m)\left(\frac{m-1}{m}\right)^{n-a}+m\),快速幂解决即可。

时间复杂度

我们可以证明,这个算法的时间复杂度是 \(O(m\log m)\)。

引理

\[\forall x\in (0,+\infty),\quad \ln(1+x)\ge \frac{x}{1+x} \]


首先考虑一个不优的策略:看到 \(m\) 就拿,否则一直跳过。这个策略在 \(k\) 个数时的期望为:

\[m\left(1-\left(\frac{m-1}{m}\right)^k\right) \]

令

\[m\left(1-\left(\frac{m-1}{m}\right)^k\right)\ge m-1 \]

得

\[\begin{aligned} m-m\left(\frac{m-1}{m}\right)^k&\ge m-1 \\ \left(\frac{m-1}{m}\right)^k&\le \frac{1}{m} \\ \left(\frac{m}{m-1}\right)^k&\ge m \\ k&\ge\log_{\frac{m}{m-1}}m \\ k&\ge\frac{\ln m}{\ln \left(1+\frac{1}{m-1}\right)} \\ \end{aligned} \]

又因为策略不优,有

\[\forall k\ge \frac{\ln m}{\ln \left(1+\frac{1}{m-1}\right)},\quad m-1\le m\left(1-\left(\frac{m-1}{m}\right)^k\right)\le f_k \]

因此

\[a\le \frac{\ln m}{\ln \left(1+\frac{1}{m-1}\right)} \]

令引理中 \(x=\frac{1}{m-1}\),得

\[\begin{aligned} \ln \left(1+\frac{1}{m-1}\right) &\ge \frac{1}{m} \\ \frac{\ln m}{\ln \left(1+\frac{1}{m-1}\right)}&\le m\ln m \\ a& \le m \ln m \end{aligned} \]

于是 DP 部分一定不会算超过 \(m\ln m\) 个,算法时间复杂度为 \(O(m\log m)\)。

Code

#include 
#define rept(i,a,b) for(int i(a);i<=b;++i)
#define db double
using namespace std;
db ksm(db x,int y){
    db res=1;
    while(y){
        if(y&1) res*=x;
        x*=x,y>>=1;
    }
    return res;
}
signed main(){
    int n,m,i=1,t;
    cin>>m>>n;
    cout<
"

原文地址: https://www.cveoy.top/t/topic/qHFN 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录