数字消失术
数字消失术
博弈论+数位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;
}

浙公网安备 33010602011771号