题目:https://www.acwing.com/problem/content/description/231/
题意:给出n堆石子,然后第一回合,A玩家可以随便拿多少堆石子,第二回合B玩家随便拿多少堆石子,第三回合开始就按照NIM博弈来进行,问第一个玩家是否可以获胜,获胜第一回合可以拿最少的火柴数是多少
思路:首先我们清楚的知道NIM博弈,获胜条件是 所有石子异或起来不能为0,也就是说第二个玩家只要能找到当前某堆石子能被其他堆石子表示出来,然后把其他不是的全部拿走就好了,所以我们第一个玩家
必须只能保留基底,其他的都不能留,这样第二个玩家就不能找到被其他表示,因为这是一个极大不相关组,要求拿走的火柴最少,也就是我们用来建基底的要更多,就从大到小排序,
1,这里回答一个疑问,为什么我们一定要把除去基底之外的所有拿走,不能留下一个,然后把能表示出他的基底拿走呢,因为我们基底是按位运算保存的.能表示出当前数,也就是说明存在这个数的二进制位异或出来的,但是这是由若干数异或出来的,可能因为要表示的数没有这个二进制位.但是他异或了两次这个位才取消掉,所以这样的做法拿的火柴数必定比直接拿走的多
#include<bits/stdc++.h> #define maxn 100005 #define mod 1000000007 using namespace std; typedef long long ll; ll a[maxn],ins[maxn]; ll k,sum; int cmp(ll x,ll y){ return x>y; } void gauss(){ for(int i=1;i<=k;i++){ ll x=a[i]; for(int j=63;j>=0;j--){ if((a[i]>>j)&1){ if(!ins[j]){ ins[j]=a[i]; sum-=x; break; } else{ a[i]^=ins[j]; } } } } printf("%lld",sum); } int main(){ scanf("%lld",&k); for(int i=1;i<=k;i++){ scanf("%lld",&a[i]); sum+=a[i]; } sort(a+1,a+k+1,cmp); gauss(); }