2020 CCPC Wannafly Winter Camp Day2 Div.1&2——C 纳新一百的石子游戏【Nimm博弈】

题目传送门


题目描述

纳新一百和乱得尬得在玩取石子的游戏。他们一共有 N N N 堆石子,第 i i i 堆有 a i a_i ai 颗石子(若 a i = 0 a_{i}=0 ai=0 则表示这是一堆空石子堆)。
纳新一百和乱得尬得轮流进行游戏,纳新一百先手。轮到某个人时,他需要选择一堆非空的石子堆,并拿走任意数量的石子。如果不存在一堆非空的石子堆,则轮到的人输掉游戏。纳新一百想要知道,他的第一轮操作有多少种不同的取法能够保证他最后取得游戏的胜利。假设两个人都是用最优策略在玩游戏,两种操作方式视为不同当且仅当两种方式选取的石子堆的序号不同或取走的石子数量不同。
为了增加趣味性,纳新一百和乱得尬得决定对前{i}i堆石子都玩一次游戏,两次游戏相互独立,也就是说,每开始一个新的游戏,石子堆都会被复原。
现在,纳新一百想要知道每一次游戏中,他能够取得胜利的第一轮操作方案数。


输入描述:

第一行一个正整数 N ( 1 ⩽ N ⩽ 1 0 5 ) N(1\leqslant N \leqslant 10^{5}) N(1N105) ,表示石子堆数。
第二行 N N N 个数,表示 a i ( 0 ⩽ a i < 2 60 ) a_{i}(0\leqslant a_{i}<2^{60}) ai(0ai<260)

输出描述:

N N N 行,每行一个整数。第 i i i 行的整数表示用前 i {i} i 堆石子玩游戏时,纳新一百在第一轮有多少种操作方式保证自己能获得胜利。如果纳新一百无论如何都不可能赢得游戏,输出 0 {0} 0


输入

6
0 0 2 8 7 0


输出

0
0
1
1
1
1


题意

  • 对于每个前缀的所有石子堆,询问先手必胜方案数。

题解

  • 设当前异或和为 x x x ,则要找到的就是有多少堆石头 y y y 满足   y > x   xor   y \ y>x\ \text{ xor }\ y  y>x  xor  y,这样就能将 y y y 变为 x x x x o r xor xor y y y 来使得异或和为 0 0 0。考虑异或和的最高的为 1 1 1 的二进制位,所有这一位是 1 1 1 y y y 显然都满足条件,这一位是 0 0 0 的都不满足条件。

AC-Code

#include
using namespace std;
typedef long long ll;

const int N=1e5+50;
ll a[N];
int sum[100];
void solve(ll x) {
	int cut=0;
	while(x) {
		if(x%2)sum[cut]++;
		x/=2;
		cut++;
	}
	return;
}
int solve2(ll x) {
	int cut=0;
	while(x) {
		cut++;
		x/=2;
	}
	return cut-1;
}
int main() {
	int n;
	scanf("%d",&n);
	for(int i=1; i<=n; ++i) {
		scanf("%lld",&a[i]);
	}
	ll ans=0;
	for(int i=1; i<=n; ++i) {
		ans^=a[i];
		solve(a[i]);
		printf("%d\n",sum[solve2(ans)]);
	}
	return 0;
}

你可能感兴趣的:(数论)