公平组合游戏博弈论+DP
可以将问题转换为一个四堆的 Nim 游戏。
Alice 可以决定两个堆的异或和 为 的任意一个值。(最简单的构造方法是第一个堆是 0,第二个堆是 ,区间就是
设第二个堆随机的区间异或和为 ,那么当 时,Alice 才会输(处于必败状态)。
最大化赢的概率等价于最小化输的概率。
那么我们需要知道对于一个固定的 ,有多少种可能的 满足 。
我们设第二段被分为两个堆 ,每一个 对代表一个可能的区间,我们现在的问题就变成了,对于给定的 ,有多少对 满足:
要满足的条件既包含异或又包含加法,考虑能否统一到二进制,这样就可以按位考虑了。
我们设 ,则 ,得到
考虑对于固定的 ,每一个合法的 可以产生多少对 。
目前的条件:
- 能被 2 整除
按位考虑,因为 ,所以对于 , 都有两种情况 ;对于 ,那么 ,此时 要么是 ,要么是 ,而对于一个确定的 , 也是确定的,所以该位只有一种情况。
因此,对于每一个合法的 ,一共会产生 1 对 。
那怎么计算有多少合法的 呢?
我们发现条件中有很多与 有关,类比 ,我们设 ,则 ,由于 能被二整除,所以 ,设 的最大值 为 ,则现在要计算有多少合法的 K 满足:
使用贪心/数位 DP 即可在 时间内计算出合法的 K,对应每一个合法的 。
与 线性相关,所以总时间复杂度为 。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 676767677;
ll dp[32];
ll D(int pos, bool limit, int maxn, int x) {
if (pos == -1) return 1;
if (!limit && dp[pos] != -1) return dp[pos];
ll res = 0;
int digit = (maxn >> pos & 1);
if (limit && !digit)
res = (res + D(pos - 1, true, maxn, x)) % MOD;
else if ((x >> pos & 1) == 1)
res = (res + D(pos - 1, limit && digit == 0, maxn, x)) % MOD;
else
res = (res + D(pos - 1, limit && digit == 0, maxn, x) +
D(pos - 1, limit && digit == 1, maxn, x) % MOD) %
MOD;
return (limit ? res : dp[pos] = res);
}
void solve() {
int x1, x2;
cin >> x1 >> x2;
array<ll, 2> ans{MOD, -1};
for (int x = 0; x < x1; x++) {
memset(dp, -1, sizeof(dp));
if (x2 - 1 - x < 0) {
ans[0] = 0;
ans[1] = x;
break;
}
int lim = (x2 - 1 - x) / 2;
int idx = 0;
while (lim >> idx) {
idx++;
}
ll pab = (1 << __builtin_popcount(x)) % MOD;
ll res = D(idx - 1, true, lim, x);
res = (res * pab) % MOD;
if (res < ans[0]) {
ans[0] = res;
ans[1] = x;
}
}
cout << 1 << ' ' << x1 - ans[1] << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) solve();
return 0;
}
Footnotes
-
Population Count,即二进制表示中 1 的个数。 ↩