新Nim游戏

[CQOI2013]新Nim游戏

题意

给定 n n n堆石子,两个人可以各取走任意堆石子,但不能取完,可以不取。接下来和 N i m Nim Nim游戏规则一样,询问是否有先手必胜策略。

思路

关于一般 N i m Nim Nim博弈游戏有这样一个结论: a 1 ⊕ a 2 ⊕ . . . ⊕ a n ! = 0 a_1\oplus a_2\oplus ... \oplus a_n!=0 a1a2...an!=0,那么先手必胜,反之如果等于0,先手必败
我们从先手的角度看:
如果我是先手,我一定不会给后手把剩余集合异或值改成 0 0 0的机会(因为后手拿掉一些元素后可能把剩余集合的异或变成 0 0 0,形成先手必败的局面)
有了这样的想法以后,我们就可以按照这个思想开始考虑做法了。

做法:

因为要最小的可能值,所以我们先贪心按照从大到小排序,然后判断每一堆插入线性基后是否异或值为 0 0 0,是就加到 a n s ans ans里面,不是就插入到线性基中。
至于贪心的证明可参照证明
附上代码,简单易懂!

代码

#include <cctype>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define ph puts("")
typedef long long ll;
template <class T>
void rd(T &x)
{
    x = 0;
    int f = 1;
    char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    x *= f;
}
template <class T>
void pt(T x)
{
    if (x < 0)
        putchar('-'), x = (~x) + 1;
    if (x > 9)
        pt(x / 10);
    putchar(x % 10 ^ 48);
}
const int N = 105;
int n, a[N], p[35];
void add(int x)
{
    for (int i = 30; ~i; i--)
        if (x & (1 << i))
        {
            if (!p[i])
            {
                p[i] = x;
                break;
            }
            x ^= p[i];
        }
    return;
}
int query(int x)
{
    for (int i = 30; ~i; i--)
        if (x & (1 << i))
            x ^= p[i];
    return x;
}
ll ans;
int main() 
{
    rd(n);
    for (int i = 1; i <= n; i++)
        rd(a[i]);
    sort(a + 1, a + n + 1);
    for (int i = n; i; i--)
        if (!query(a[i]))
            ans += a[i];
        else
            add(a[i]);
    pt(ans);
    return 0;
}

end!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值