把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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,所以可以联想到状态压缩。 
*/
posted @ 2024-11-24 20:54  high_skyy  阅读(46)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end