【BZOJ3105】[CQOI2013] 新Nim游戏(贪心+线性基)
大致题意: 两个人玩新\(Nim\)游戏。一开始二人先各有一次拿走若干整堆石子的机会(可以不拿,但不能拿完),然后再进行普通的\(Nim\)游戏。问先手在保证胜利的情况下,一开始拿走的石子总数最小值。
前言
\(Jan\ 29th\)刷题计划(3/6),算法标签:贪心、线性基。
遇上重题了\(233\):【BZOJ2460】 [BeiJing2011] 元素。
双倍经验
做这道题之前,可以先去看一看另一道题:【BZOJ2460】 [BeiJing2011] 元素。
回来看这道题,\(Nim\)游戏有一个基本结论:当各堆石子数异或值不为\(0\)时,先手必胜。
也就是说,先手在拿走若干整堆石子后,要让后手无论如何拿去剩下的石子,都无法使各堆石子数异或值为\(0\)。
拿去最少,就是留下最多。于是题目就被转化为:选出最多的石子,使得这些石子堆不存在一个子集使得子集中各堆石子个数异或和为\(0\)。
这样一来就和上面给出的那道题完全一致了,直接按照那道题的做法,贪心+线性基即可。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100
#define LV 32
#define LL long long
using namespace std;
int n,s[N+5],v[N+5];
I bool Ins(RI x) {for(RI i=LV;~i;--i) if((x>>i)&1) {if(!v[i]) return v[i]=x;else x^=v[i];}return 0;}//插入线性基
int main()
{
RI i;LL t=0;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",s+i),t+=s[i];//统计石子总数
for(sort(s+1,s+n+1),i=n;i;--i) Ins(s[i])&&(t-=s[i]);return printf("%lld",t),0;//从大到小枚举,能留下就留下
}
待到再迷茫时回头望,所有脚印会发出光芒