CF1628D2

回顾

首先我们先回顾一下简单版:

设 $dp[i][j]$ 表示 $i$ 次操作,至少有 $j$ 次要加 $t$

首先加一下初始化:

  • $dp[i][0] = 0$,即当要加 $0$ 次时,Bob 一定会狂减,那 Alice 每次选 $0$ 最优

  • $dp[i][i] = i \times k$,即 Bob 要全加,那 Alice 每次选 $k$ 最优

接下来我们来考虑怎么转移,假设我们准备转移 $dp[i][j]$

当选到第 $i$ 轮时,Bob 有两种选法:

  • 加 $t$,因为 Bob 一定只会加 $j$,也就是说 Bob 以前会加 $j - 1$ 次,所以转移过来的数为 $dp[i - 1][j - 1] + t$
  • 减 $t$,同理, Bob 以前会加 $j$ 次,所以转移过来的数为 $dp[i - 1][j] - t$

所以,Alice 会选使得 $\min (dp[i - 1][j - 1] + t, dp[i - 1][j] - t)$ 最大的 $t$,那么$$ dp[i][j] = \max_{0 \leq l \leq k} \{\min (dp[i - 1][j - 1] + l, dp[i - 1][j] - l)\} $$ 但若暴力枚举 $l$ ,肯定会超时,考虑优化

容易发现,当 $l= \dfrac{dp[i - 1][j - 1] + dp[i - 1][j]}{2}$ 时,

$\min (dp[i - 1][j - 1] + l, dp[i - 1][j] - l) = \dfrac{dp[i - 1][j - 1] + dp[i - 1][j]}{2}$ 取的值最大,

所以,总结一下得到:$$ dp[i][j]=\begin{cases}0&j = 0\\i\times k&j =i\\\dfrac{dp[i - 1][j - 1] + dp[i - 1][j]}{2}&1 \leq j \leq i - 1\end{cases} $$ 因为有 $T$ 组询问,所以若每次都做一次,复杂度为 $O(Tnm)$,肯定会超时,

但唯独 $k$ 在转移中出现,我们就考虑可不可以在转移中出掉 $k$,

经过一番思考,显然,$dp[j][j] = k \times f[i][j]$ ,为什么呢?

因为在转移的过程中,唯有额外的常数,而初始化都可以表示成上面的式子,

$f[i][j]$ 怎么转移呢?很简单,只要把上面的 $f[i][i] = i$ 就行了,总结一下,$$ f[i][j] = \begin{cases}0&j=0\\i&j=i\\\dfrac{f[i - 1][j - 1] + dp[i - 1][j]}{2} & 1\leq j \leq i - 1\end{cases} $$ 最后查询的时候输出 $k \times f[n][m]$ 即可。

思路分析

我们先不考虑除以 $2$ ,

可以发现 $f[n][m]$ 一定是由 $f[1][1] \sim f[m][m]$ 所表示,即可以表示成:$$ f[n][m] = \sum_{i=1}^mh(i)\times f[i][i] $$

显然,$h(i)$ 的值相当于在一个矩形中点 $(i, i)$ 走到 $(n, m)$ 的方案数,

再由转移方程式中的 $f[i - 1][j - 1] $ 与 $f[i - 1][j]$,得到只能往下或右下走,

但这些路径不能到其他形如 $(a, a)$ 的点,不然就算重复了,怎么处理呢?

第一步一定向下走,就可以了,证明如下:

  • 首先第一步不可能王右下走,要往下走
  • 接下来我们尽可能接近 $(a, a)$ 点,显然 $(a, a)$ 点一定在当前点的右边,那么我们往右下走,但无论怎么走,也最多会走到 $(a, a - 1)$

因为每一局都会往下走,所以会走 $n - i$ 步,往右下一共走 $m - j$ 步,

又因为第一步只能往下走,那么就从 $n - i - 1$ 步中,选出 $m - j$ 步走右下,显然是一个组合数

总结一下:路径总数 $= h(i) = \dbinom{n - i - 1}{m - j}$

回到 $f[n][m]$,把 $h(i)$ 带入:$$ f[n][m] = \sum_{i=1}^m i\times \dbinom{n - i - 1}{m - j} $$ 考虑回除以 $2$,上文已说:会走 $n - i$ 步。

而每走一步都会除以 $2$,所以最终会除以 $2^{n-i}$,

直接写会答案形式:$$ ans = k\sum_{i=1}^m\dfrac{i \times \binom{n-i-1}{m-j}}{2^{n-i}} $$ 复杂度 $O(Tm)$

#include <cstdio>
#include <iostream>
using namespace std;
#define int long long

const int maxn = 1e6 + 10, mod = 1e9 + 7;
int inv[maxn], fac[maxn];

int ksm (int x, int y) {
    int res = 1;
    while (y) {
        if (y & 1) res = 1ll * res * x % mod;
        x = 1ll * x * x % mod; y >>= 1; 
    }
    return res;
}

void init (int n) {
    fac[0] = 1;
    for (int i = 1; i <= n; ++i) fac[i] = 1ll * fac[i - 1] * i % mod;
    inv[n] = ksm (fac[n], mod - 2);
    for (int i = n - 1; i >= 0; --i) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod; // 求 i! 的逆元

}

int C (int n, int m) { // 求组合数
    if (n < m) return 0;
    if (n == m || m == 0) return 1;
    return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
}

signed main() {
    init(maxn - 5);
    int T; scanf ("%lld", &T);
    while (T --) {
        int n, m, k; scanf ("%lld%lld%lld", &n, &m, &k);
        int res = 0;
        if (n == m) { // 特判
            printf ("%lld\n", n * k % mod);
        } else {
            for (int i = 1; i <= m; ++i) {
                res = (res + i * C (n - i - 1, m - i) % mod * ksm (ksm (2, mod - 2), n - i) % mod) % mod;
            }
            printf ("%lld\n", res * k % mod);
        }
    }
    return 0;
}
posted @ 2022-01-27 22:11  wangzhongyuan  阅读(2)  评论(0编辑  收藏  举报  来源