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)尽可能大,从二进制高位到低位进行遍历,考虑以下四种情况:
- (D_i=1,X_i=1),一定被之前(D&x>0)判断了,所以这里不会存在。
- (D_i=1,X_i=0),此时一定(a_i=b_i=1)
- (D_i=0,X_i=1),此时一定(a_i+b_i=1),为了(mathit a)尽量大,若(a+2^ileq A_1),则令该位为1.
- (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;
}