Atcoder ABC172 F - Unfair Nim(NIM博弈+二进制运算)

题目链接: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 AB=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=ab+2(ab)

这样我们就可以得到
{ 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. ABAB==y2(xy)

x − y 2 = X , y = Y \frac{x-y}{2} = X , y = Y 2xy=X,y=Y(只是为了后续书写看起来舒服点)
这里我们就可以看到 x > y x > y x>y || ( x − y ) (x - y) (xy)% 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+2i1<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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值