题目链接:https://atcoder.jp/contests/abc172/tasks/abc172_f
题意:给你 n n n堆石子,每堆石子有 a [ i ] a[i] a[i]个。现在两个人玩游戏,每次可以重一推石子中取走 1 1 1个, 2 2 2个,或者把这一堆石子全部取走。现在在游戏开始之前,你可以将第一堆中的若干( 1 − > a [ 1 ] − 1 1->a[1]-1 1−>a[1]−1)个石子放到第二堆,问你最少放多少个可以保证后手必赢,如果不行的话,输出 − 1 -1 −1。
思路:首先我们知道这是一个 n i m nim nim游戏,结论就是 a [ 1 ] ⨁ a [ 2 ] ⨁ a [ 3 ] . . . ⨁ a [ n ] = 0 a[1] \bigoplus a[2]\bigoplus a[3] ... \bigoplus a[n]=0 a[1]⨁a[2]⨁a[3]...⨁a[n]=0的话,后手必败。所以这一题题意就可以转化成,求解最小的 a n s ans ans,使得 ( a [ 1 ] − a n s ) ⨁ ( a [ 2 ] + a n s ) ⨁ a [ 3 ] . . . ⨁ a [ n ] = 0 (a[1]-ans) \bigoplus (a[2]+ans)\bigoplus a[3] ... \bigoplus a[n]=0 (a[1]−ans)⨁(a[2]+ans)⨁a[3]...⨁a[n]=0 那该怎么求呢? 1 e 12 1e12 1e12的数据范围,暴力是不可能的啦… 我们设 A = a [ 1 ] − a n s , B = a [ 2 ] + a n s A = a[1] - ans,B = a[2] + ans A=a[1]−ans,B=a[2]+ans,这样我们就可以可以知道 A ⨁ B = a [ 3 ] ⨁ . . . ⨁ a [ n ] = y A \bigoplus B = a[3] \bigoplus... \bigoplus a[n]=y A⨁B=a[3]⨁...⨁a[n]=y A + B = a [ 1 ] + a [ 2 ] = x A+B = a[1] + a[2] = x A+B=a[1]+a[2]=x
现在就需要一个非常有用的公式 a + b = a ⨁ b + 2 ( a ⋀ b ) a + b = a \bigoplus b + 2(a \bigwedge b) a+b=a⨁b+2(a⋀b)
这样我们就可以得到 { A ⨁ B = y A ⋀ B = ( x − y ) 2 \left\{ \begin{aligned} A \bigoplus B & = & y \\ A \bigwedge B & = & \frac{(x - y)}2 \end{aligned} \right. ⎩⎪⎨⎪⎧A⨁BA⋀B==y2(x−y)
另 x − y 2 = X , y = Y \frac{x-y}{2} = X , y = Y 2x−y=X,y=Y(只是为了后续书写看起来舒服点) 这里我们就可以看到 x > y x > y x>y || ( x − y ) (x - y) (x−y)% 2 ! = 0 2 !=0 2!=0,那可定要输出-1。 我们用 X i , Y i , A i , B i X_i,Y_i,A_i,B_i Xi,Yi,Ai,Bi表示分别表示 X , Y , A , B X,Y,A,B X,Y,A,B第 i i i位二进制数,现在我们就是要找到 A < a [ 1 ] A < a[1] A<a[1]的最大值,可以看到 A m i n = X A_{min} = X Amin=X,那我们接下来就看一下 X i , Y i X_i,Y_i Xi,Yi四种情况下 A A A的值有啥变化。 X i = 1 X_i = 1 Xi=1 && Y i = 1 Y_i = 1 Yi=1, A i , B i A_i,B_i Ai,Bi无解,输出-1; X i = 1 X_i = 1 Xi=1 && Y i = 0 Y_i = 0 Yi=0, A i = 1 , B i = 0 A_i = 1,B_i = 0 Ai=1,Bi=0 || A i = 0 , B i = 1 A_i = 0,B_i = 1 Ai=0,Bi=1,因为我们要让 A A A尽量大,我们就要在 A + 2 i − 1 < a [ 1 ] A + 2^{i-1} < a[1] A+2i−1<a[1]的情况下让 A i = 1 A_i = 1 Ai=1; X i = 0 X_i = 0 Xi=0 && Y i = 1 Y_i = 1 Yi=1, A i = 1 , B i = 1 A_i = 1,B_i=1 Ai=1,Bi=1, A A A值不变; X i = 0 X_i = 0 Xi=0 && Y i = 0 Y_i = 0 Yi=0, A i = 0 , B i = 0 A_i=0,B_i=0 Ai=0,Bi=0, A A A值不变;
这样,这道题就可以解出来啦,具体操作详见代码。
AC代码:
#define _CRT_SECURE_NO_WARNINGS 1 #include <set> #include <map> #include <stack> #include <queue> #include <cmath> #include <ctime> #include <vector> #include <cstdio> #include <string> #include <iomanip> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define LL long long #define pii pair<int,int> #define sd(x) scanf("%d",&x) #define slld(x) scanf("%lld",&x) #define pd(x) printf("%d\n",x) #define plld(x) printf("%lld\n",x) #define rep(i,a,b) for(int i = (a) ; i <= (b) ; i++) #define per(i,a,b) for(int i = (a) ; i >= (b) ; i--) #define mem(a) memset(a,0,sizeof(a)) #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 #define fast_io ios::sync_with_stdio(false) const LL mod = 1e9 + 7; const int maxn = 5e5 + 7; const int INF = 0x3f3f3f3f; const double pi = acos(-1.0); int main() { #ifndef ONLINE_JUDGE // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout); long _begin_time = clock(); #endif int n; sd(n); LL a, b; slld(a), slld(b); LL sum = a + b; LL Xor = 0; rep(i,3,n) { LL x; slld(x); Xor ^= x; } bool flag = false; if(sum < Xor || (sum - Xor) % 2 != 0) flag = true; LL And = sum - Xor >> 1; LL ans = And; per(i,60,0) { int xx = ((And >> i) & 1LL); int yy = ((Xor >> i) & 1LL); if(xx && yy) { flag = true; break; } if(!xx && yy && ((ans | (1LL << i)) <= a)) { ans |= (1LL << i); } } if(!ans || ans > a || flag) puts("-1"); else plld(a-ans); #ifndef ONLINE_JUDGE long _end_time = clock(); // cout << "time = " << _end_time - _begin_time << endl; #endif return 0; }