数字消失术

数字消失术

博弈论+数位dp

  • 题目大意: Alice 和 Bob 轮流从一个正整数中删除一个数字。每次删除后,产生的所有前导零会被一并删除(例如从 105 中删除百位的 1 得到 5,删除十位的 0 得到 15,删除个位的 5 得到 10)。
    删除最后一个非零数字的玩家获胜。
    定义 ( W(N) ) 为所有小于 ( N ) 的正整数中,在双方都采取最优策略下先手玩家必胜的数的个数。
    给定 ( N ),请你计算 ( W(N) )。

  • 输入描述
    一行一个整数 ( N )(( 1 \le N \le 10^{18} ))。

  • 输出描述
    一行一个整数,表示 ( W(N) )。

  • 数据范围
    ( 1 \le N \le 10^{18} )

    先考虑没有0的情况,显然长度为奇数的为必胜态。
    接下来考虑有0存在的情况,就相当于在d * 10^n + r 这里的状态就取决于后续r的状态。如果r是奇数,就是必胜,此时如果第一步去掉d,就变成了,胜态,所以先手不会这么做,这就取决于中间0的数量,如果0的数量是奇数,就是必胜态,偶数则是必败态。而r为偶数则相反。
    由此可见,只要n的长度为奇数,则总是必胜态。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

ll dp[20][2], sum[20][2], pw[20];//pw 10的幂次,dp 位置i的输赢数量,sum 到i的输赢数量

//两个函数的写法 可以参考
// ll win(ll n);
// ll lose(ll n) {//先手输的数量
//     if(n <= 1) return 0;
//     string s = to_string(n);
//     int len = s.size();
//     ll ans = sum[len-1][0];

//     if(len % 2 == 0) {
//         int first = n / pw[len-1];
//         ans += (first-1) * sum[len-1][1];
//         ll r = n % pw[len-1];
//         ans += win(r);
//     }
//     return ans;
// }
// ll win(ll n) {//先手赢的数量
//     if(n <= 1) return 0;
//     return n - 1 - lose(n);
// }
一个函数的写法
ll cal(ll n) {
    if(n <= 1) return 0;
    string s = to_string(n);
    int len = s.size();

    ll ans = sum[len - 1][1];
    int first = s[0] - '0';
    ll r = n % pw[len - 1];

    if(len % 2 == 1) {
        ans += (first - 1) * pw[len - 1];
        ans += r;
    }
    else {
        ll loseCnt = sum[len - 1][0];
        loseCnt += (first - 1) * sum[len - 1][1];
        loseCnt += cal(r);
        ans = (n - 1) - loseCnt;
    }
    return ans;
}
void solve() {
    pw[0] = 1;
    for(int i = 1; i < 20; i ++) pw[i] = pw[i-1] * 10;

    for(int i = 1; i < 20; i ++) {
        ll tol = pw[i - 1] * 9;
        if(i & 1) dp[i][1] = tol, dp[i][0] = 0;
        else dp[i][0] = sum[i-1][1] * 9, dp[i][1] = tol - dp[i][0];
        
        sum[i][0] = sum[i-1][0] + dp[i][0];
        sum[i][1] = sum[i-1][1] + dp[i][1];
    }

    ll n; cin >> n;
    cout << cal(n) << endl;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr);
    solve();
    return 0;
}
posted @ 2026-09-08 20:00  thenrify  阅读(15)  评论(0)    收藏  举报