Revenge of Nim II
Problem Description
Nim is a mathematical game of strategy in which two players take turns removing objects from distinct heaps. On each turn, a player must remove at least one object, and may remove any number of objects provided they all come from the same heap.
---Wikipedia
Today, Nim takes revenge on you, again. As you know, the rule of Nim game is rather unfair, only the nim-sum (⊕) of the sizes of the heaps is zero will the first player lose. To ensure the fairness of the game, the second player has a chance to move some (can be zero) heaps before the game starts, but he has to move one heap entirely, i.e. not partially. Of course, he can’t move all heaps out, at least one heap should be left for playing. Will the second player have the chance to win this time?
Input
The first line contains a single integer T, indicating the number of test cases.
Each test case begins with an integer N, indicating the number of heaps. Then N integer Ai follows, indicating the number of each heap.
[Technical Specification]
1. 1 <= T <= 100
2. 1 <= N <= 1 000
3. 1 <= Ai <= 1 000 000 000 000
Output
For each test case, output “Yes” if the second player can win by moving some (can be zero) heaps out, otherwise “No”.
Sample Input
3
1
2
3
2 2 2
5
1 2 3 4 5
Sample Output
No
Yes
Yes
题目大意:
Nim的复仇。有N堆石子,后手可以在游戏开始前拿走任意堆石子再进行Nim博弈,问后手是否有机会赢。
解题目标:
先来复习一下Nim博弈。
给定N堆石子,假设第i堆石子有a[i]个石头子,两个人轮流从某一堆中取任意多的物品,规定每一次至少取一个,多者不限,最后取光者胜。
Nim博弈的常规解法:k=a[1]^a[2]^a[3]^....^a[n]
若k==0 先手必输
若k!=0 后手必输
显然这个题的目标就是 找出石子堆集合的一个子集(子集元素个数大于等于2),使其元素异或之后为0即可。
解题思路:
错误思路:
做比赛的时候,想着用随机化算法来水过去,循环了350W次,提交了3遍。。 发现N<=1000,随机化算法不可能过。。
正确思路:
大牛的blog就是不一般。思路就一两句话,研究了整整一天,才勉强看懂。(一大牛题解就三个字:位运算。。Orz)
根据官方题解的思路来看,要把这些数字看成一个二进制矩阵
该矩阵具有一下性质:
(1)每一个空都是1或0
(2)相对于只有1或0两个值的矩阵元素,那么异或运算相当于其加减法:1+1=0 1+0=1 0+1=1 0+0=0 1-1=0 1-0=1 0-1=1 0-0=0
再根据矩阵的秩与极大无关组的相关定理可知:若矩阵的秩rank<N,那么极大无关组的成员数要小于N,那么对于任意一个a[i](特别对于不属于极大无关组的a[i])都可以用极大无关组中的成员来进行表示。
在根据性质:对于任意数a,a^a=0可知,当且仅当rank<N时,存在一个子集使其异或值为0。
解题方法:
据说是高斯消元。将矩阵化成梯形,那么存在空行,即rank<N。
ps:前面说了,异或在二进制中相当于加减。与矩阵的初等变化不矛盾。
Code:
/*************************************************************************
> File Name: BestCode#16_1003.cpp
> Author: Enumz
> Mail: 369372123@qq.com
> Created Time: 2014年11月01日 星期六 17时44分16秒
************************************************************************/ #include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<list>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
#include<bitset>
#include<time.h>
#include<climits>
#define MAXN 3000
using namespace std;
long long a[MAXN];
int main()
{
int T;
cin>>T;
while (T--)
{
int N;
cin>>N;
for (int i=;i<=N;i++)
scanf("%I64d",&a[i]);
int row=,col=;
for ( ;row<=N&&col<=;col++,row++) //有数据范围可知,化成二进制之后,列数不会超过40
{
int tmp_row;
for (tmp_row=row;tmp_row<=N;tmp_row++) //找到当前列下的第一个不为零的值
if (a[tmp_row]&(1LL<<col)) //判断是否为1
break;
if (tmp_row==N+) //不存在为1的点,说明存在一个空行,rank--
{
row--;
continue;
}
swap(a[tmp_row],a[row]); //交换行
for (int i=tmp_row+;i<=N;i++)
if (a[i]&(1LL<<col))
a[i]^=a[row];
}
if (row<N)
printf("Yes\n");
else
printf("No\n");
}
return ;
}