[SPOJ IGAME Interesting Game]Nim 博弈+数位DP

[SPOJ IGAME Interesting Game]Nim 博弈+数位DP

分类:博弈 数位DP

1. 题目链接

[SPOJ IGAME Interesting Game]

2. 题意描述

Alice和Bob玩游戏,一个数字,对于其在十进制下的每一位,玩家可以选择一个数值非0的位,将这个位置的数减去一个非0的数,使得这个位置的数在操作后非负。在某个玩家操作后,所有位置的数字都变为了0,则这个玩家获胜。
假定Alice和Bob都做出最优选择,且Alice先手。
分别输出,在A,B之间,Alice能获胜的数字有多少,Bob能获胜的数字有多少。
数据范围:
T,测试样例数。
1 <= T <= 10000, 1 <= A <= B <= 1e18.

3. 解题思路

首先,很容易观察到,对于某一数字,判断是否是先手获胜还是后手获胜,就是一个裸的Nim博弈。那么,后手胜,当且仅当该数字十进制所有位异或值为0。
那么现在的问题就是,找出 [A,B] 区间中十进制位异或值为 0 <script type="math/tex" id="MathJax-Element-6">0</script>的个数。数位DP随便搞搞就好了。需要记录的状态有: 当前位pos,当前异或值status。
只要不像我一样,简单的题目犯低级错误,应该就没有什么问题啦。

4. 实现代码

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

typedef long long LL;
typedef long double LB;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;

const int INF = 0x3f3f3f3f;
const LL INFL = 0x3f3f3f3f3f3f3f3fLL;
const LB eps = 1e-8;
const int BITS = 18 + 5; /// 十进制长度
const int STATES = 15 + 5; /// 异或值

LL A, B;
int dig[BITS];
LL dp[BITS][STATES];

LL dfs(int pos, int states, bool limit) {
    if(pos < 1) return states == 0;
    if(!limit && dp[pos][states] != -1) return dp[pos][states];
    int lst = limit ? dig[pos] : 9;
    LL ret = 0;
    for(int i = 0; i <= lst; ++i) ret += dfs(pos - 1, states ^ i, limit && (i == lst));
    if(!limit) dp[pos][states] = ret;
    return ret;
}

LL solve(LL x) {
    int len = 0;
    while(x > 0) {
        dig[++ len] = x % 10;
        x /= 10;
    }
    return dfs(len, 0, true);
}

int main() {
#ifdef ___LOCAL_WONZY___
    freopen("input.txt", "r", stdin);
#endif // ___LOCAL_WONZY___
    memset(dp, -1, sizeof(dp));
    int _; scanf("%d", &_);
    while(_ --) {
        scanf("%lld %lld", &A, &B);
        LL Bob = solve(B) - solve(A - 1), Alice = (B - A + 1) - Bob;
        printf("%lld %lld\n", Alice, Bob);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值