Codeforces Round 941 (Div. 2)

88 阅读2分钟

C. Everything Nim

题面

image.png

大意:

两个玩家轮流取石子 ,每次可以选择对每个石子堆移除k个石子 ,其中 k 不超过当前最小石子堆的数量。 Alice 先手 , 最后谁没有石子可以移除了,那么另外一个人胜利了。

思路

题意要求我们,每次最多只能给每个非空堆移除k个,k不超过当前最小非空堆数量

  • 从边界条件考虑 , 什么才会约束我们 ?

    "1" , 这个数字导致我们只能被迫给每个堆移除一个

给出样例

1 2 3

对于这样的样例 ,我们只能 A (Alice) | B (Bob) 轮流移除

A 1     ----> 1 2
B 1 	----> 0 1
A 1 	----> 0 0
B lose

我们发现每次移除 "1" 总是会约束我们 , 这也是我们一招制胜的《关键》

  1. 谁取到第一个大于1的石子堆 , 谁就赢了
  • 情况 1

    2 3 4 5 ........ , 这样总是 A 掌握主动权(因为A第一个取到当前最小的大于1的石子堆) , 移除 1 个 -----> 1 2 3 4 ....... , 然后 B 只能慢性死亡 , 因为每次我们总是控制B只能移除 1 个 , 到最后一个 , A 无情全部取完 , B 卒。

  • 情况 2 1 2 3 4 5 10 ..... , 前面几个总是轮流取的。那谁第一个取到大于 1 数量的最小石子堆 , 他就掌握主动权 , 策略就即情况一的方法。

  1. 但是这里还有一个因素需要考虑(我称之为时也命也)
样例 1 : 1 2 3 4 5 
样例 2 : 1 2 3 4

上述两个样例 , 谁都无法决定 , 只能轮流去等谁最后无法移除石子。 即总是只能取到数量为 1 的石子堆 , 那就要看数据的奇偶。

总结:

  1. 在取完最后一个之前取到大于1的最小石子堆
    • 如果数量为 1 的数量为 奇数个 1 2 3 5 Bob win
    • 如果数量为 1 的数量为 偶数个 1 2 5 Alice win
  2. 取完最后一个之前都只能取到数量为1的最小石子堆
    • 如果石子堆的数量为奇数个 Alice win
    • 如果石子堆的数量为偶数个 Bob win

参考代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>

#define LL long long
#define PII std::pair<int , int>

const int N = 100010;

void solved(){

	int n , max = 0;
	std::cin >> n;
	std::set<int> s;
	for (int i = 1; i <= n; i ++ ){
		int x; std::cin >> x;
		s.insert(x);
		max = std::max(max , x);
	}

	int last = 0;
	for (int k : s){
		if (k != last + 1)
			break;
		last = k;
	}

	if (last == max){						
		if (last % 2 == 1)
			std::cout << "Alice" << '\n';
		else
			std::cout << "Bob" << '\n';
	}else{
		if (last % 2 == 1)
			std::cout << "Bob" << '\n';
		else
			std::cout << "Alice" << '\n';
	}
}


int main(){

    std::ios::sync_with_stdio(0);
	std::cin.tie(0);
	std::cout.tie(0);

	int tc = 1;
	std::cin >> tc;
	while (tc -- )
		solved();

	return 0;
}