QOJ 7606 Digital Nim

Tag:博弈论 + 数位 DP。

不难想到直接设 \(sg_i\) 表示 \(i\)\(\text{SG}\) 值,设 \(S(i)\) 表示 \(i\)数位和
则有 \(sg_i = \text{mex} _{j=1}^{S(i)} \{ sg_{i-j} \}\)
询问的 \(n \leq 10^{18}\),暴力算复杂度上天,打表也找不出什么规律。

但现在关键在于不需要知道确切的 \(\text{SG}\),只需要求出是否必败
不妨设 \(pre_i\) 表示 \(i\) 和上一个必败态 \(j\) 的距离 \((i-j)\)
只要满足 \(pre_i \gt S(i)\),那么 \(i\) 就是必败态

目前仍然没有什么进展,但 \(n \leq 10^{18}\) 可以考虑数位 DP
考虑数位 DP 的本质:快速计算大量重复或相似的状态。
例如对 \(135 \dots\)\(531 \dots\) 这两个数,前面三位已经确定,且数位和相等,对后面位填数的限制也是相同的。
所以目前影响状态的因素有数位和、剩余位数

要用数位 DP 计算什么
\(d\) 表示到区间左端点最近的必败态距离,我们希望快速算出 \(+k \times 10^t\) 之后的 \(d'\)(到右端点最近必败态距离)
所以还有影响因素距离 \(dist\)

综上所述,设 \(dp_{u,sum,dist}\) 表示剩余 \(u\) 位,已经确定的部分数位和为 \(sum\),最近必败态到左端点距离 \(dist\),转移到 \(+10^u\) 位置的距离是多少。
转移只要枚举下一位填 \(i\),把 \(dp_{u-1,sum+i,}\) 得到的结果首尾拼起来即可。

#include <bits/stdc++.h>
using namespace std;
const int N = 19, M = 185;
int T; long long n;
int s[N], c[N];
int dp[N][M][M];

inline int S(long long x) { int res = 0; while (x) res += x % 10, x /= 10; return res; }
int DP(int u, int sum, int dist) {
    if (!u) return (dist >= sum) ? 1 : dist + 1;
    if (~dp[u][sum][dist]) return dp[u][sum][dist];
    int now = dist;
    for (int i = 0; i <= 9; i++) now = DP(u - 1, sum + i, now);
    return dp[u][sum][dist] = now;
}

int main() {
    memset(dp, -1, sizeof dp);
    scanf("%d", &T);
    while (T--) {
        scanf("%lld", &n), n++; // 计算 (n+1) 到 pre 的距离
        int dist = 1; long long x = n;
        for (int i = 0; i <= 18; i++) s[i] = S(x), c[i] = x % 10, x /= 10;
        for (int i = 18; i >= 0; i--) {
            for (int j = 1; j <= c[i]; j++) dist = DP(i, s[i + 1] + j, dist);
        }
        puts((dist > 1) ? "Algosia" : "Bajtek");
    }
    return 0;
}
posted @ 2025-06-25 15:06  Conan15  阅读(48)  评论(1)    收藏  举报