P2575 高手过招 题目分析
P2575 高手过招 题目分析
分析题目性质
我们发现每个行之间是互不干预的,因此可以分开处理。
又注意到:\(m\leq 20.\)
我们不难由此想到用状态压缩来表示 \(SG\) 函数。
于是问题就迎刃而解了。
思路
根据上述我们预处理出 \(SG\) 函数,然后对于第 \(i\) 个数据的第 \(j\) 行对其状态的 \(SG\) 值进行异或,判断是否为 \(0\) 就能确定答案。
细节
对于实现 \(SG\) 函数,我们可以考虑枚举状态,然后枚举哪一个位置有石头并将其移动。
于是就有:
\[SG_{i}=\text{mex}_{k\in S}\{SG_{k}\}
\]
其中 \(S\) 表示为进行变化过后的状态。
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stdlib.h>
#include <cstring>
#include <vector>
#define N 1005
#define M 1500005
using namespace std;
const int m = 20;
int T,n,sg[M],len[N],p[N];// SG代表了这个状态的必胜必败态
int b[25],cnt;
bool vis[M];
signed main(){
cin >> T;
for (int i = 1;i < m;i ++) sg[(1 << i) - 1] = 0;
sg[0] = 1;
for (int i = 2;i < (1 << m);i ++) {
int cnt = 0;
for (int j = 0;j < m && (1 << j) <= i;j ++)
if ((i >> j) & 1) {
int t = (i ^ (1 << j));
int k;
for (k = j - 1;((i >> k) & 1) && k > 0;k --);
if (k < 0) continue;
if (k == 0 && ((i >> k) & 1)) continue;
vis[sg[t | (1 << k)]] = 1;
b[++cnt] = sg[t | (1 << k)];
}
for (int j = 0;;j ++)
if (!vis[j]) {
sg[i] = j;
break;
}
for (int i = 1;i <= cnt;i ++)
vis[b[i]] = 0;
}
for (;T--;) {
scanf("%d",&n);
for (int i = 1;i <= n;i ++) {
scanf("%d",&len[i]);
p[i] = 0;
for (int j = 1;j <= len[i];j ++) {
int x;
scanf("%d",&x);
int t = m - x;
p[i] |= (1 << t);
}
}
int ans = 0;
for (int i = 1;i <= n;i ++) ans ^= sg[p[i]];
if (ans) puts("YES");
else puts("NO");
}
return 0;
}
/*
2
1
2 19 20
2
1 19
1 18
不难发现每一行都是独立的。
因此答案应该是每一行的 SG 相互异或。
考虑到 m <= 20,所以可以联想到状态压缩。
*/

浙公网安备 33010602011771号