Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Forget the actual stone values for a second. For any threshold , ask only: "can Alice force the final pile to have at least stones?" Then each pile is just a bit: good-for-Alice if , bad otherwise.
The game value sum can be recovered from thresholds:
So the total answer is the sum, over all thresholds , of the number of configurations where Alice can force a final pile with value at least .
For a fixed bitmask of piles whose values are at least , the minimax result is only a Boolean: can the current player path end with some pile from ? Let mean Alice can force success from the remaining original piles mask, assuming turns are determined by how many piles were already removed.
When a pile is removed, the remaining piles keep their relative order, so an original pile is removable iff its current rank inside mask is in the good-index set. If it is Alice's turn, she needs at least one removable move to a winning state. If it is Bob's turn, Alice needs every removable move to stay winning.
After you can classify every high/low mask as winning or losing, count how many value assignments produce exactly that for threshold : piles in have choices, the others have . So you need
Group winning masks by popcount. That's the hard-version unlock.
Let the pile values be . The deletion rules depend only on positions, but the players' goals depend on the final value. Directly simulating minimax over all values is dead on arrival: configurations, welcome to pain.
The clean way out is to turn values into thresholds.
For any final value ,
So instead of summing final values directly, we can sum, for every threshold , how many configurations let Alice force the final pile to have value at least .
For a fixed , each pile becomes binary:
Alice wants the final pile to be high. Bob wants the final pile to be low. Now the actual values are irrelevant. The whole game is just: given a subset of original pile indices marked high, can Alice force the final remaining pile to belong to ?
Game DP on Remaining Piles
Represent the remaining original piles by a bitmask . Their current order is the natural order of original indices still present.
A move may remove the pile whose current rank is in the good-index set. Example: if remaining original piles are , then original pile currently has rank .
Let mean: starting from all piles present, with high piles exactly , Alice can force the final pile to be high.
To compute this, define a recursive minimax state for this fixed :
From state , the legal moves are exactly the remaining piles whose current rank is marked good.
If it is Alice's turn, she wants success, so
If it is Bob's turn, he wants Alice to fail, so Alice succeeds only if Bob has no escape:
The answer for high set is .
Now doing this separately for all with a fresh recursive DP would be too much. But and the total over tests is bounded, so we can compute the result for all at once using bitsets.
Bitset DP Over All High Sets
For every remaining-piles mask , store a bitset over all possible high masks :
means Alice can force a high final pile from state when the high set is .
Base case: if has one remaining pile , then Alice wins exactly when . Therefore is the bitset of all containing bit .
Transition:
This is nice because the same game graph works for every . We only combine whole bitsets.
There are at most states, and each bitset has bits, so the rough cost is bit operations. For , this is fine in C++ if implemented sanely. Big, but not stupid-big.
Counting Configurations
After the DP, tells us exactly which high masks are winning for Alice.
For a fixed threshold , if the high mask is :
So the number of configurations producing this is
Only matters for this count. Let
Then the answer is
Reindex , so goes from to and :
Since and the total is at most , evaluating this directly over all and all is completely fine.
Why This Actually Matches the Original Game
The only subtle point is thresholding. Minimax with numeric values can look scary because Alice and Bob compare actual pile values, not just high/low labels.
But the identity
lets us decompose the numeric payoff into independent Boolean games. For each threshold , Alice maximizing is equivalent to Alice trying to make , and Bob minimizing is equivalent to Bob trying to make it . Deterministic finite zero-sum games with perfect information behave perfectly under this threshold view: for each , either Alice can force a final pile at least , or Bob can prevent it.
Then we count all assignments that make that Boolean win happen. No probability hand-waving, no bogus linearity assumption. Just threshold sums doing the heavy lifting.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
static const int MOD = 1000000007;
int addmod(int a, int b) {
int s = a + b;
if (s >= MOD) s -= MOD;
return s;
}
int mulmod(ll a, ll b) {
return int(a * b % MOD);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, m;
cin >> n >> m;
int k;
cin >> k;
vector<int> goodRank(n + 1, 0);
for (int i = 0; i < k; i++) {
int x;
cin >> x;
goodRank[x] = 1;
}
int N = 1 << n;
int W = (N + 63) >> 6;
vector<int> pc(N), lg(N, -1);
for (int i = 1; i < N; i++) {
pc[i] = pc[i >> 1] + (i & 1);
lg[i] = lg[i >> 1] + 1;
}
vector<vector<unsigned long long>> dp(N, vector<unsigned long long>(W));
vector<unsigned long long> contains(n, vector<unsigned long long>(W).front());
contains.assign(n, vector<unsigned long long>(W, 0));
for (int s = 0; s < N; s++) {
int w = s >> 6, b = s & 63;
for (int i = 0; i < n; i++) {
if (s >> i & 1) contains[i][w] |= 1ULL << b;
}
}
for (int mask = 1; mask < N; mask++) {
if ((mask & (mask - 1)) == 0) {
int i = __builtin_ctz(mask);
dp[mask] = contains[i];
}
}
vector<int> masks(N - 1);
iota(masks.begin(), masks.end(), 1);
sort(masks.begin(), masks.end(), [&](int a, int b) {
return pc[a] < pc[b];
});
for (int mask : masks) {
int sz = pc[mask];
if (sz <= 1) continue;
bool alice = ((n - sz) % 2 == 0);
if (!alice) {
for (int w = 0; w < W; w++) dp[mask][w] = ~0ULL;
}
int rank = 0;
for (int i = 0; i < n; i++) {
if (!(mask >> i & 1)) continue;
rank++;
if (!goodRank[rank]) continue;
int child = mask ^ (1 << i);
if (alice) {
for (int w = 0; w < W; w++) dp[mask][w] |= dp[child][w];
} else {
for (int w = 0; w < W; w++) dp[mask][w] &= dp[child][w];
}
}
}
int full = N - 1;
vector<int> cnt(n + 1, 0);
for (int s = 0; s < N; s++) {
if ((dp[full][s >> 6] >> (s & 63)) & 1ULL) {
cnt[pc[s]]++;
}
}
int ans = 0;
vector<int> powA(n + 1), powB(n + 1);
for (int b = 0; b <= m - 1; b++) {
int lo = b;
int hi = m - b;
powA[0] = 1;
powB[0] = 1;
for (int i = 1; i <= n; i++) {
powA[i] = mulmod(powA[i - 1], hi);
powB[i] = mulmod(powB[i - 1], lo);
}
for (int j = 0; j <= n; j++) {
if (!cnt[j]) continue;
int ways = mulmod(powA[j], powB[n - j]);
ans = (ans + (ll)cnt[j] * ways) % MOD;
}
}
cout << ans << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
static const int MOD = 1000000007;
int addmod(int a, int b) {
int s = a + b;
if (s >= MOD) s -= MOD;
return s;
}
int mulmod(ll a, ll b) {
return int(a * b % MOD);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, m;
cin >> n >> m;
int k;
cin >> k;
vector<int> goodRank(n + 1, 0);
for (int i = 0; i < k; i++) {
int x;
cin >> x;
goodRank[x] = 1;
}
int N = 1 << n;
int W = (N + 63) >> 6;
vector<int> pc(N), lg(N, -1);
for (int i = 1; i < N; i++) {
pc[i] = pc[i >> 1] + (i & 1);
lg[i] = lg[i >> 1] + 1;
}
vector<vector<unsigned long long>> dp(N, vector<unsigned long long>(W));
vector<unsigned long long> contains(n, vector<unsigned long long>(W).front());
contains.assign(n, vector<unsigned long long>(W, 0));
for (int s = 0; s < N; s++) {
int w = s >> 6, b = s & 63;
for (int i = 0; i < n; i++) {
if (s >> i & 1) contains[i][w] |= 1ULL << b;
}
}
for (int mask = 1; mask < N; mask++) {
if ((mask & (mask - 1)) == 0) {
int i = __builtin_ctz(mask);
dp[mask] = contains[i];
}
}
vector<int> masks(N - 1);
iota(masks.begin(), masks.end(), 1);
sort(masks.begin(), masks.end(), [&](int a, int b) {
return pc[a] < pc[b];
});
for (int mask : masks) {
int sz = pc[mask];
if (sz <= 1) continue;
bool alice = ((n - sz) % 2 == 0);
if (!alice) {
for (int w = 0; w < W; w++) dp[mask][w] = ~0ULL;
}
int rank = 0;
for (int i = 0; i < n; i++) {
if (!(mask >> i & 1)) continue;
rank++;
if (!goodRank[rank]) continue;
int child = mask ^ (1 << i);
if (alice) {
for (int w = 0; w < W; w++) dp[mask][w] |= dp[child][w];
} else {
for (int w = 0; w < W; w++) dp[mask][w] &= dp[child][w];
}
}
}
int full = N - 1;
vector<int> cnt(n + 1, 0);
for (int s = 0; s < N; s++) {
if ((dp[full][s >> 6] >> (s & 63)) & 1ULL) {
cnt[pc[s]]++;
}
}
int ans = 0;
vector<int> powA(n + 1), powB(n + 1);
for (int b = 0; b <= m - 1; b++) {
int lo = b;
int hi = m - b;
powA[0] = 1;
powB[0] = 1;
for (int i = 1; i <= n; i++) {
powA[i] = mulmod(powA[i - 1], hi);
powB[i] = mulmod(powB[i - 1], lo);
}
for (int j = 0; j <= n; j++) {
if (!cnt[j]) continue;
int ways = mulmod(powA[j], powB[n - j]);
ans = (ans + (ll)cnt[j] * ways) % MOD;
}
}
cout << ans << '\n';
}
return 0;
}