E1. Prime Gaming (Easy Version)
E1. Prime Gaming (Easy Version)
This is the easy version of the problem. The difference between the versions is that in this version, $m \le 2$. You can hack only if you solved all versions of this problem.
A valid configuration is defined as an arrangement of $n$ piles of stones such that:
- The number of stones in each pile is an integer between $1$ and $m$ (both inclusive).
Given a valid configuration of $n$ piles of stones, some indices from $1$ to $n$ are marked as good. Alice and Bob start playing a game taking $n-1$ turns alternately with Alice going first. In each turn, they have to perform the following operation:
- Choose any integer $i$ such that $1 \le i \le p$ (where $p$ is the number of piles left) and $i$ is good, and remove the $i$-th pile completely.
Note that after performing the operation once, the number of piles decreases by $1$ and the remaining piles are re-indexed. The game will end when there is only one pile left. It is guaranteed that the index $1$ is always good.
Let $x$ denote the number of stones in the final remaining pile. Alice wants to maximize $x$, whereas Bob wants to minimize it. Both Alice and Bob play optimally.
Find the sum of $x$ over all the possible valid configurations modulo $10 ^ 9 + 7$.
Input
Each test contains multiple test cases. The first line contains the number of test cases $t$ ($1 \le t \le 10^4$). The description of the test cases follows.
The first line of each testcase contains two integers $n$ ($1 \le n \le 20$) and $m$ ($1 \le m \le 2$) — the number of piles and the upper bound on the number of stones in a pile.
The second line of each testcase contains a single integer $k$ ($1 \le k \le n$) — the number of indices marked as good.
The third line of each testcase contains $k$ integers $c_1,c_2,\ldots,c_k$ ($1=c_1<c_2<\ldots<c_k\le n$) — the good indices. It is guaranteed that $1$ is always a good index (i.e. $c_1=1$).
It is guaranteed that the sum of $2^n$ over all test cases does not exceed $2^{20}$.
It is guaranteed that the sum of $m$ over all test cases does not exceed $10^6$.
Output
For each testcase, print a single integer — the sum of $x$ over all the possible valid configurations modulo $10 ^ 9 + 7$.
Example
Input
3
2 2
1
1
3 1
2
1 3
3 2
2
1 2
Output
6
1
11
Note
For the first test case, the valid configurations are: $[1, 1]$, $[1,2]$, $[2, 1]$, $[2, 2]$.
As there are only $2$ piles of stones, Alice can only choose the first pile and remove it. Thus, the sum of $x$ is equal to $1+1+2+2=6$.
For the second test case, the final pile is always $1$.
解题思路
首先如果 $m=1$,则答案恒为 $1$。这是因为此时每堆石子的数量均为 $1$,不管怎么取最终剩下的那堆石子的数量也只能是 $1$。
接下来我们考虑 $m=2$ 的情况。由于 $n \leq 20$ 很小,很容易想到先暴力枚举 $2^n$ 种初始状态,再根据每种初始状态求解最终剩下的石子的数量。由于我们已经用 $O(2^n)$ 的复杂度枚举了所有的初始状态,因此只能用 $O(n)$ 的方法来对每个初始状态进行求解。这意味着我们只能采用贪心策略或寻找某种规律,但这种方式并不容易直接想到(也不一定有)。此外,我们知道,如果将每个局面看作节点,局面之间的转移看作边,我们就能得到一个有向无环图。通过从最终状态(即只剩一堆石子的局面)反向推导出每个状态对应的最终结果,进而得到初始状态的结果。但遍历整个图的复杂度是 $O(2^n)$,总的复杂度就是 $O(4^n)$,不可行。
但是再细想一下就会发现,虽然初始状态有 $2^n$ 种,但游戏过程中会有大量的中间状态重复。比如初始状态 $[1,2,1,1]$ 与 $[1,1,2,1,1]$ 在取走石子后,都会经过状态 $[1,1,1]$、$[1,2,1]$ 和 $[2,1,1]$。因此,如果对每个初始状态都单独求解,许多中间状态将被重复计算。事实上,根据局面剩余石子堆数进行分类,实际上所有可能的状态只有 $\sum\limits_{i=1}^{n}{2^i} = \sum\limits_{i=0}^{n}{2^i} - 1 = 2^{n+1}-2$ 种。因此我们可以在计算出每个节点的结果后进行记忆化,从而避免重复遍历这些节点。
定义状态 $f(i,j,k)$,其中为当前石子堆数 $i$,$j$ 为当前局面状态($j$ 的二进制前位 $i$ 中,$1$ 表示该堆石子数量为 $2$,$0$ 表示该堆石子数量为 $1$),$k$ 为当前玩家($1$ 表示 Alice,$0$ 表示 Bob)。状态 $f(i,j,k)$ 表示在局面 $j$ 下,玩家 $k$ 先手能获得的最大结果。根据选择取走石子堆的位置进行状态转移,状态转移方程为 $$\begin{cases} f(i,j,k) = \max_{c_u \leq i}\{f(i-1,g(j,c_u),k \oplus 1) \}, &k=1 \\ f(i,j,k) = \min_{c_u \leq i}\{f(i-1,g(j,c_u),k \oplus 1) \}, &k=0 \\ \end{cases}$$
其中 $g(j,c_u)$ 表示从状态 $j$ 取走第 $c_u$ 堆石子后的状态,对应 $j$ 的二进制中,移除第 $c_u$ 位并将第 $c_u + 1$ 位及之后的位依次向低位移动。对应的代码实现为 (j & (1 << c[u] - 1) - 1) | (j >> c[u] << c[u] - 1)。
最后答案就是 $\sum\limits_{i=0}^{2^n - 1}{f(n,i,1)}$。
AC 代码如下,时间复杂度为 $O(n 2^n)$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 25, M = 1 << 20;
int n, m, l;
int c[N];
int f[N][M][2];
int get(int x, int k) {
return (x & (1 << k - 1) - 1) | (x >> k << k - 1);
}
int dfs(int i, int j, int k) {
if (i == 1) return f[i][j][k] = (j & 1) + 1;
if (abs(f[i][j][k]) <= 2) return f[i][j][k];
for (int u = 0; u < l; u++) {
if (c[u] > i) break;
int t = dfs(i - 1, get(j, c[u]), k ^ 1);
if (k) f[i][j][k] = max(f[i][j][k], t);
else f[i][j][k] = min(f[i][j][k], t);
}
return f[i][j][k];
}
void solve() {
cin >> n >> m >> l;
for (int i = 0; i < l; i++) {
cin >> c[i];
}
if (m == 1) {
cout << 1 << '\n';
}
else {
for (int i = 1; i <= n; i++) {
for (int j = 0; j < 1 << i; j++) {
f[i][j][0] = 0x3f3f3f3f;
f[i][j][1] = -0x3f3f3f3f;
}
}
int ret = 0;
for (int i = 0; i < 1 << n; i++) {
ret += dfs(n, i, 1);
}
cout << ret << '\n';
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
参考资料
Codeforces Round 1049 (Div. 2) Editorial:https://codeforces.com/blog/entry/146218
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/19097307

浙公网安备 33010602011771号