AtCoder Beginner Contest 172.F - Unfair Nim
思路:异或的性质+构造。
显然题目背景是游戏,我们目的是让后手胜,显然堆石子异或为时,后手必胜。接下我们需要构造来使堆石子异或和为。
因为题目要求我们只能将移动第一堆石子给第二堆石子。
我们记第一堆和第二堆石子个数分别为。
所以我们可以预处理第堆到第堆石子的异或和,我们记为。
设需要移动的石子数为个。
即我们要使:
首先我们需要知道几个结论:
1:
即个数异或和不能大于其求和。显然取等当这个个数两两的所在位不同。
2.异或和与求和奇偶性相同。
3.要构造长度为2的数组。
最开始满足的条件是
当且仅当,长度才能变为2。即两个数1的位完全不同,因此才能满足.
以上证明如果不懂可以看看之前的博客写的一个类似的题,相信会有更清楚的理解。
博客链接
因此我们可以先排除一些不可能的答案。
记
是不可能的情况。
第一个对应性质2,第二个对应性质1,第三个是要当前最小的构造必须不能大于,
第四个是性质3.
因为题目是要求拿走的石子数最少,即要在的情况下最大。
因为前一个数的所在位,后面全部有,且的1位不同。(性质3)
所以我们只需要从里拿过来给前面一个数,让变大。
所以枚举一下的1的位就解决了此题。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7; #define mst(a) memset(a,0,sizeof a) #define lx x<<1 #define rx x<<1|1 #define reg register #define PII pair<int,int> #define fi first #define se second int main(){ int n; ll a,b,c=0; scanf("%d%lld%lld",&n,&a,&b); for(int i=3;i<=n;i++){ ll x; scanf("%lld",&x); c^=x; } ll s=a+b,d=(s-c),ans=d/2; if((d&1)||d<0||ans>a||(ans&c)) return puts("-1"),0; for(ll i=(1LL<<61);i;i>>=1){ if((i&c)&&ans+i<=a) ans+=i; } printf(ans?"%lld\n":"-1\n",a-ans); return 0; }