3105: [cqoi2013]新Nim游戏
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 535 Solved: 317
[Submit][Status][Discuss]
Description
传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同)。两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿。拿走最后一根火柴的游戏者胜利。
本题的游戏稍微有些不同:在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。
如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。
Input
第一行为整数k。即火柴堆数。第二行包含k个不超过109的正整数,即各堆的火柴个数。
Output
输出第一回合拿的火柴数目的最小值。如果不能保证取胜,输出-1。
Sample Input
6
5 5 6 6 5 5
5 5 6 6 5 5
Sample Output
21
HINT
k<=100
才学了拟阵,然后现学现用,居然就发现了这么一道拟阵好题。
题目简化出来大概是你要在一堆数集中,删除一些数,使得剩下的数不可能组成异或和为0的非空集合,并且使删除的数尽量的小。考虑依次加数,每次加完异或高消一次,如果发现线性相关,那么就不加入这个数,否则加入。这样可以满足删除的数的个数尽量小。
然而题目中求得是删除的数字的和,怎么办?注意到这道题所说的“子集异或和非0集合”其实就是一个拟阵。【拟阵(E,I)1.空集满足 2.若集合A满足,则A子集满足“子集异或和非0” 3.若card(A)>card(B),存在x属于A,且B+{x}属于I,这个可以用异或高消来理解】所以按照拟阵的标准贪心思路,将读入的数列从大到小排一遍序就ok了。
发现以前写的异或高消都有bug,整个人都splay起来了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define MAXN 100000 typedef long long qword; int lst[MAXN]; inline bool cc(int x,int y) { for (int i=30;i>=0;i--) if (x&(1<<i)) return y&(1<<i); } int main() { freopen("input.txt","r",stdin); int n; scanf("%d",&n); for (int i=0;i<n;i++) scanf("%d",lst+i); sort(lst,lst+n,greater<int>()); qword ans=0; int x,y; for (int i=0;i<n;i++) { x=lst[i]; for (int j=0;j<i;j++) { if (cc(lst[j],lst[i])) lst[i]^=lst[j]; } if (!lst[i]) ans+=x; } printf("%lld\n",ans); }