MENU

Solution. LG4301 [CQOI2013] 新Nim游戏

Description

$k$ 堆石子,两人轮流操作,第一轮中,双方可以拿走若干整堆的石子,可以一堆不拿,不能拿走所有的堆,接下来的游戏规则和 nim 游戏一样。

如果后手必胜,输出 $-1$。否则,输出在先手必胜的情况下,先手第一回合至少要拿的石子个数。

$k\le100$

Solution

不难发现先手一定存在必胜策略,只需拿的只剩一堆即可,所以 $-1$ 情况是不存在的。

nim 游戏的结论是,先手必败当且仅当石子数的异或和为 $0$,要想先手必胜,我们只需让第一回合过后,不存在若干堆石子的石子数异或和为 $0$。

考虑用线性基解决这一问题。

假设现在我们有一个线性基,已经被成功插入的数的集合为 $S$,如果一个数 $x$ 无法被插入线性基,说明这个数能够用 $S$ 中的若干个数异或表示出来,i.e. $S\cup\{x\}$ 中存在若干个数异或和为 $0$。

那么,我们只需要按照某种顺序将每堆的石子个数插入线性基,无法成功插入的堆就是我们需要拿走的堆。

一个显而易见的贪心是从大到小插入,但这样做的正确性保障呢?

如果插入一个大数 $a$ 会导致多于一个小数 $b_{1\dots k}$ 无法插入,我们的贪心就可能是错误的,只需证明不存在这种情况。

具体的,导致多于一个小数无法插入指的是:

  • 如果插入 $a$,则 $b_{1\dots k}$ 无法插入线性基。

  • 如果不插入 $a$,则 $b_{1\dots k}$ 能够插入线性基。

前者显然是可能发生的,考虑 $b_{1\dots k}$ 中的一个数 $b_i$,令线性基中已经被成功插入的数的集合为 $S$,不妨设 $b_i=\bigoplus_{x\in S_i}x$,$a\in S_i$,$S_i\setminus a\subseteq S$。

此时,如果不插入 $a$,我们将 $b_i$ 插入线性基,$S$ 变为 $S\cup\{b_i\}$,由 $b_i$ 的表示,其张成的线性空间等价于 $S\cup\{a\}$,那么对于任意一个其他的数 $b_j$,其仍然无法插入线性基。

那么,我们可以得到,插入一个大数,至多会导致一个小数无法插入,于是我们就证明了贪心的正确性。

Code

namespace Main{
    const int N = 105;
    const int L = 30;
    ll k, sum, a[N];
    struct linearBasis{
        int t[L];
        bool insert(int x){
            int save = x;
            for(ri i = L - 1; i >= 0; i--){
                if(!(x >> i & 1)) continue;
                if(!t[i]) return t[i] = x, true;
                else x ^= t[i];
            }
            return false;
        }
    }lb;
    void Main(){
        input(k);
        for(ri i = 0; i < k; i++) input(a[i]);
        sort(a, a + k, greater<int>());
        for(ri i = 0; i < k; i++) if(!lb.insert(a[i])) sum += a[i];
        write(sum);
        return;
    }
} // namespace
Last Modified: November 25, 2022
Archives Tip
QR Code for this page
Tipping QR Code