AtCoder Regular Contest 091F

AtCoder Regular Contest 091F - Strange Nim

题目大意

  • N N N堆石子,每堆有 A i A_i Ai颗,两个人轮流取,每次选择其中一堆,当其剩下石子数为 x x x时,取的石子数不能超过 x / k x/k x/k下取整,每堆的 k k k是一个定值,不能取的人为负。
  • N ≤ 200 N≤200 N200
  • A i , k i ≤ 1 0 9 A_i,k_i≤10^9 Ai,ki109

题解

  • 显然可以先计算出对于不同 k k k的每个状态的SG函数值,从 0 0 0开始打个表出来,
  • 把这些数 k k k个一行,会发现第一列从 0 0 0开始依次递增 1 1 1,第一行全为 0 0 0,当把第一列的数全部删去后,剩下的数也满足这个规律,
  • 那么一开始先把 A i A_i Ai加一,再这样一直删下去就可以求出当前 A i A_i Ai的函数值。
  • 一开始以为这样就可以过了,其实发现是会TLE的,
  • 有可能 A i A_i Ai特别大,但 A i / k i A_i/k_i Ai/ki也特别大,以至于每次只能删去很少的几个数,需要重复操作很多遍,
  • 那么直接考虑一次性多删一些。
  • 每次相当于给 A i A_i Ai减去 ( A i − 1 ) / k (A_i-1)/k (Ai1)/k下取整,当 ( A i − 1 ) / k (A_i-1)/k (Ai1)/k下取整的值没变时,每次减去的数值就是一样的,那么直接就算最多可以先重复删多少个这个值即可。

代码

#include<cstdio>
using namespace std;
int solve(int x, int k) {
	if(x <= k) return 0;
	if((x - 1) % k == 0) return (x - 1) / k;
	int d = (x - 1) / k * k + 1, t = (x - 1) / k + 1;
	return solve(x - ((x - 1 - d) / t + 1) * t, k);
}
int main() {
	int n, s = 0, x, k;
	scanf("%d", &n);
	while(n--) {
		scanf("%d%d", &x, &k);
		s ^= solve(x + 1, k);
	}
	if(s) printf("Takahashi"); else printf("Aoki");
	return 0;
}
哈哈哈哈哈哈哈哈哈哈
原文地址:https://www.cnblogs.com/LZA119/p/13910017.html