新Nim遊戲

[CQOI2013]新Nim遊戲

題意

給定nn堆石子,兩個人可以各取走任意堆石子,但不能取完,可以不取。接下來和NimNim遊戲規則一樣,詢問是否有先手必勝策略。

思路

關於一般NimNim博弈遊戲有這樣一個結論:a1a2...an!=0a_1\oplus a_2\oplus ... \oplus a_n!=0,那麼先手必勝,反之如果等於0,先手必敗
我們從先手的角度看:
如果我是先手,我一定不會給後手把剩餘集合異或值改成00的機會(因爲後手拿掉一些元素後可能把剩餘集合的異或變成00,形成先手必敗的局面)
有了這樣的想法以後,我們就可以按照這個思想開始考慮做法了。

做法:

因爲要最小的可能值,所以我們先貪心按照從大到小排序,然後判斷每一堆插入線性基後是否異或值爲00,是就加到ansans裏面,不是就插入到線性基中。
至於貪心的證明可參照證明
附上代碼,簡單易懂!

代碼

#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!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章