AtCoder Beginner Contest 172-F

AtCoder Beginner Contest 172-F - Unfair Nim(nim博弈,二进制构造)

题意:

(mathit n)堆石子,每一堆有(A_i)个石头,问最少从第一堆中拿出多少个石子(也不能拿完,即拿出的石子数为([0,A_1-1]))放在第二堆中可以使(mathit n)堆石子的nim博弈后手必赢?

思路:

使(mathit n)堆石子的nim博弈后手必赢条件为:

(A_1oplus A_2oplus A_3oplus dots oplus A_n=0)

即:

(A_1oplus A_2= A_3oplus dots oplus A_n)

设:

(X= A_3oplus dots oplus A_n)

(S=A_1+A_2)

所以此题可以转化为:

找出两个整数:(a,b,ain[1,A_1-1])

满足:

  • (a+b=S)

  • (aoplus b=X)

(mathit b)尽量小,(mathit a)尽量大,

我们知道(a+b=aoplus b+2*(a&b))

那么(aoplus b+2*(a&b)=X+2*(a&b)=S)

(a&b= frac{(S-X)}{2})

(D=frac{(S-X)}{2})

如果(D<0 or (D&X)>0)则一定是无解的,

(mathit a)的最小值就是(mathit D),所以如果(D>A_1)也是无解的。

接下来可以对每一个二进位进行单独考虑:

(a,b,D,X)的二进制表示法中第(mathit i)位为(a_i,b_i,D_i,X_i)

接下来为使得答案 (mathit a)尽可能大,从二进制高位到低位进行遍历,考虑以下四种情况:

  1. (D_i=1,X_i=1),一定被之前(D&x>0)判断了,所以这里不会存在。
  2. (D_i=1,X_i=0),此时一定(a_i=b_i=1)
  3. (D_i=0,X_i=1),此时一定(a_i+b_i=1),为了(mathit a)尽量大,若(a+2^ileq A_1),则令该位为1.
  4. (D_i=0,X_i=0)此时一定(a_i=b_i=0)

此时判断下是否满足(a>0),即(b<A_1),若为真,答案就是(A_1-a=b)

否则输出无解。

代码:

int n;
ll a[maxn];
int main()
{
#if DEBUG_Switch
    freopen("C:\code\input.txt", "r", stdin);
#endif
    //freopen("C:\code\output.txt","r",stdin);
    cin >> n;
    repd(i, 1, n)
    {
        cin >> a[i];
    }
    ll x = 0ll;
    repd(i, 3, n)
    {
        x ^= a[i];
    }
    ll s = a[1] + a[2];
    ll d = s - x;
    if (d < 0 || (d & 1))
    {
        cout << -1 << endl;
    } else
    {
        d /= 2;
        if (d > a[1] || (d & x) > 0)
        {
            cout << -1 << endl;
        } else
        {
            ll aa = d;
            for (int i = 60; i >= 0; --i)
            {
                if (x & (1ll << i))
                {
                    if (aa + (1ll << i) <= a[1])
                    {
                        aa += (1ll << i);
                    }
                }
            }
            if (aa == 0)
            {
                cout << -1 << endl;
            } else
            {
                cout << a[1] - aa << endl;
            }
        }
    }
    return 0;
}