题解 - Game on Sum (Easy Version)
题目,还是不挂洛谷。
Alice 镇楼
题目大意
Alice 和 Bob 又在玩游戏。
该种游戏共分为 \(n\) 轮,每轮中,先有一个数 \(x = 0\),Alice 将选择 \(t \isin [0, t_{max}]\),而 B 将会选择将 \(x\) 增加或减少 \(t\)。在全部 \(n\) 轮中,B 应至少有 \(m\) 次选择减少操作。
Alice 希望结果最大,而 B 希望结果最小,并且他们都会做出最优决策。
求问最终 \(x\) 的值。
思路简析
感觉这个很博弈论。
每步最优操作,要推 dp 的样子还是比较显然的吧?
这里只有 \(n, m\) 是给定变量,所以显然以其为状态。
考虑 \(f_{i, j}\) 的一个值即进行了 \(i\) 轮时,已经进行 \(j\) 次增加操作时的 \(x\) 值。
边界为 \(f_{i, 0} = 0, f_{i, i} = i \times k\)(当 \(j = 0\) 时,Alice 必选 \(0\),当 \(j = i\) 时,Alice 必选 \(k\))。
由于是 B 进行操作,那么他必然是取小,易得转移方程:
\(f_{i, j} = \min{f_{i-1, j}-t, f_{i-1, j-1}+t}\)
\(Range : i_{1\rightarrow n}, j_{1\rightarrow \min{i-1, m}}\)
考虑 Alice 要尽量大,那么就不能让 B 选出最小的,所以她会使 \(f_{i-1, j}-t = f_{i-1, j-1}+t\),整理得 \(t = \frac{f_{i-1, j}-f_{i-1, j-1}}{2}\),那么 \(f_{i, j} = f_{i-1, j-1}+t = \frac{f_{i-1, j}+f_{i-1, j-1}}{2}\)。
最终结果是 \(f_{n, m}\)。
然后我们写完了就会发现这是显然不对的,因为样例里有分数,而 \(f_{i, j}\) 显然无法存入分数。
这个怎么办?一开始我还想分开存分子和分母,于是突击乘法逆元,然后你看定义:
如果一个线性同余方程 \(ax \equiv 1 \pmod b\) ,则 \(x\) 称为 \(a \bmod b\) 的逆元,记作 \(a^{-1}\) 。
那我们正好就是要把这个数乘上 \(2^{-1}\) 的。所以要找到 \(2x \equiv 1 \pmod {10^9+7}\),得 \(x = 5\times10^8+4\),所以把每个 \(f_{i, j}\) 都乘上 \(5\times10^8+4\) 即可(相当于除以 \(2\))。
(\(2147482647 \div (5\times10^8+4) \approx 4\),所以在代码里这里要乘上 1LL
。)
但是你考虑这样的话时间复杂度是 \(O(T\cdot n\cdot m)\) 即 \(4\times 10^9\) 的。
怎么办怎么办,但是你仔细看一眼这里除了 \(k\) 真有啥是必要的吗?显然对于这个 dp \(n\) 和 \(m\) 只是一个范围,那么我们只要把范围推够了即可。那么我们可以预先钦定一个 \(k = 1\),然后对于每一问进行对 \(k\) 的一个乘,即是答案(这样的话上面转移方程的范围 \(i\) 应是到 \(n_{max}\) ,\(j\) 也可以去掉 \(m\) 了,这很好。)。
点击查看代码
#include <bits/extc++.h>
namespace {
using namespace std;
using namespace __gnu_pbds;
#define fiin(x) freopen(x".in", "r", stdin)
#define fiout(x) freopen(x".out", "w", stdout)
#define files(x) fiin(x), fiout(x)
#define und unsigned
#define ll long long
#define db double
#define pii pair<int, int>
#define mp(x, y) make_pair(x, y)
#define m1p(x, y) ((x<<14)+y)
#define fir first
#define sec second
#define hap gp_hash_table
// #define pri_que
const int man = 2e3+10, mop = 1e9+7, inv = 5e8+4;
}
int T, n, m, k, li, lj;
int f[man][man];
int main () {
files("test");
scanf("%d", &T);
for (int i = 1; i <= 2e3; ++ i) {
f[i][i] = i;
for (int j = 1; j < i; ++ j)
f[i][j] = 1LL*(f[i-1][j]+f[i-1][j-1])*inv%mop;
}
while (T --) {
scanf("%d%d%d", &n, &m, &k);
printf("%lld\n", 1LL*f[n][m]*k%mop);
} return 0;
}