Live2D

Solution -「HNOI 2007」「洛谷 P3185」分裂游戏

\(\mathcal{Description}\)

  Link.

  给定 \(n\) 堆石子,数量为 \(\{a_n\}\),双人博弈,每轮操作选定 \(i<j\le k\),使 \(a_i \leftarrow a_i-1\)\(a_j \leftarrow a_j+1\)\(a_k \leftarrow a_k+1\),并保证操作后所有 \(a_i\ge0\)。求保证先手胜的第一步操作方案数和字典序最小的第一步操作。

  多测,\(n\le21\)\(0\le a_i\le10^4\),数据组数 \(\le10\)

\(\mathcal{Solution}\)

  由于每次只能取走一个石子,所以一个有 \(x\) 个石子的位置实际上可以看做 \(x\) 堆互不相关的石子放在同一个位置。而由于“互不相关”,求出每个位置上有一颗石子的 SG 函数异或起来就是答案。令 \(\operatorname{sg} (i)\) 表示位置 \(i\) 有一颗石子的 SG 值,显然:

\[ \operatorname{sg} (i)=\operatorname{mex}_{i<j\le k}\{\operatorname{sg} (j)\oplus\operatorname{sg}(k)\} \]

  扫出 \(\operatorname{sg}\),设所有石子 \(\operatorname{sg}\) 异或和为 \(X\),据此判断是否有解。若有解,暴力枚举第一次操作的 \(i,j,k\),若 \(X\oplus \operatorname{sg} (i)\oplus \operatorname{sg} (j)\oplus \operatorname{sg} (k)=0\),说明操作后先手必败,此次操作计入贡献,最终 \(\mathcal O(Tn^3)\) 就解决啦!

\(\mathcal{Code}\)

/* Clearink */

#include <cstdio>
#include <cstring>

const int MAXN = 21;
int n, sg[MAXN + 5], a[MAXN + 5];

inline int calcSG ( const int i ) {
	if ( ~sg[i] ) return sg[i];
	bool vis[105] {};
	for ( int j = i + 1; j <= n; ++ j ) {
		for ( int k = j; k <= n; ++ k ) {
			vis[calcSG ( j ) ^ calcSG ( k )] = true;
		}
	}
	for ( int j = 0; ; ++ j ) if ( !vis[j] ) return sg[i] = j;
}

int main () {
	int T;
	for ( scanf ( "%d", &T ); T --; ) {
		scanf ( "%d", &n ), memset ( sg, 0xff, sizeof sg );
		int ans = 0;
		for ( int i = 1; i <= n; ++ i ) {
			if ( scanf ( "%d", &a[i] ), a[i] & 1 ) {
				ans ^= calcSG ( i );
			}
		}
		if ( !ans ) { puts ( "-1 -1 -1\n0" ); continue; }
		int ways = 0;
		for ( int i = 1; i <= n; ++ i ) {
			if ( !a[i] ) continue;
			for ( int j = i + 1; j <= n; ++ j ) {
				for ( int k = j; k <= n; ++ k ) {
					if ( !( ans ^ calcSG ( i ) ^ calcSG ( j ) ^ calcSG ( k ) ) && !ways ++ ) {
						printf ( "%d %d %d\n", i - 1, j - 1, k - 1 );
					}
				}
			}
		}
		printf ( "%d\n", ways );
	}
	return 0;
}
posted @ 2020-09-29 21:31  Rainybunny  阅读(117)  评论(0编辑  收藏  举报