MYF

HDU 5088 Revenge of NIM II

题目链接

HDU 5088

题目类型:高斯消元求解异或方程组

题目来源:BestCoder #16

题目分析

题目大意

Nim的复仇。

Nim博弈的规则,一个人可以从n堆石子中选任意一堆取走至少一个。现在,后手可以移走这n堆中的任意几堆,也可以不移走,要问后手是否能胜?

解析

要想后手胜,则需要使先手达到必败态,nim博弈的性质是,如果所有数的异或和为0,则先手必败,否则先手必胜。那么我们只需要保留一些堆,使这些堆异或为0即可保证先手必败,后手必胜。先看数据范围是10^12,也就是说,最多不会超过2^40次方,我们选取最长宽度也就是MAXN为40即可。当数据大于40时,也必然会有高斯消元之后全为0的行,所以当n>40时,直接认为可以后手赢。当n<40的时候要去算矩阵的秩是否小于未知量的个数,也就是说,自由未知量的个数是否大于0,如果大于零则可以保证能使行阶梯矩阵至少一行为0。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <set>
#include <map>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <vector>
#include <iomanip>
#include <bitset>
#include <cstring>
#include <iostream>
#include <deque>
#include <algorithm>
#define Memset(a,val) memset(a,val,sizeof(a))
#define PI acos(-1)
#define PB push_back
#define MP make_pair
#define rt(n) (i == n ? '\n' : ' ')
#define hi printf("Hi----------\n")
#define IN freopen("input.txt","r",stdin);
#define OUT freopen("output.txt","w",stdout);
#define debug(x) cout<<"Debug : ---"<<x<<"---"<<endl;
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef pair<int,int> PII;
typedef long long ll;
const int MAXN=40+5;
const int mod=1000000007;
const int INF=0x3f3f3f3f;
const double eps=1e-8;
//有equ个方程,var个变元。增广矩阵行数为equ,列数为var+1,分别为0到var
int equ,var;
int a[MAXN][MAXN]; //增广矩阵
int x[MAXN]; //解集
int free_x[MAXN];//用来存储自由变元(多解枚举自由变元可以使用)
int free_num;//自由变元的个数
//返回值为-1表示无解,为0是唯一解,否则返回自由变元个数
int Gauss()
{
int max_r,col,k;
free_num = 0;
for(k = 0, col = 0 ; k < equ && col < var ; k++, col++)
{
max_r = k;
for(int i = k+1;i < equ;i++)
{
if(abs(a[i][col]) > abs(a[max_r][col]))
max_r = i;
}
if(a[max_r][col] == 0)
{
k--;
free_x[free_num++] = col;//这个是自由变元
continue;
}
if(max_r != k)
{
for(int j = col; j < var+1; j++)
swap(a[k][j],a[max_r][j]);
}
for(int i = k+1;i < equ;i++)
{
if(a[i][col] != 0)
{
for(int j = col;j < var+1;j++)
a[i][j] ^= a[k][j];
}
}
}
for(int i = k;i < equ;i++)//进入此循环的条件:本身矩阵行大于列 或者 因为出现自由变元后使得非自由变元数比行数小
if(a[i][col] != 0)//若等号右边是1则无解,因为等号左边已经消为0
return -1;//无解
if(k < var) return var-k;//自由变元个数
//唯一解,回代
for(int i = var-1; i >= 0;i--)
{
x[i] = a[i][var];
for(int j = i+1;j < var;j++)
x[i] ^= (a[i][j] && x[j]);
}
return 0;
}
int main(){
int t, n;
ll tmp;
scanf("%d",&t);
while (t--) {
memset(a, 0, sizeof(a));
scanf("%d",&n);
if (n>40) {
while (n--) {
scanf("%*lld");
}
printf("Yes\n");
}
else
{
for (int i=0; i<n; i++) {
scanf("%lld",&tmp);
for (int j=0; j<MAXN; j++) {
if (tmp>>j&1) {
a[j][i]=1;
}
}
}
equ = MAXN;
var = n;
if (Gauss()>0) {
printf("Yes\n");
}
else
printf("No\n");
}

}
}