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

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

题意:

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

思路:

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

\(A_1\oplus A_2\oplus A_3\oplus \dots \oplus A_n=0\)

即:

\(A_1\oplus A_2= A_3\oplus \dots \oplus A_n\)

设:

\(X= A_3\oplus \dots \oplus A_n\)

\(S=A_1+A_2\)

所以此题可以转化为:

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

满足:

  • \(a+b=S\)

  • \(a\oplus b=X\)

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

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

那么\(a\oplus 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^i\leq 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;
}



posted @ 2020-07-03 01:47  茄子Min  阅读(293)  评论(0编辑  收藏  举报