【ARC091D】 Strange Nim

题目

显然对每一堆石子求一个SG之后异或起来就好了。

这个SG看起来只能\(O(n^2)\)的样子啊,考虑找规律;

一下是\(k=3\)时的一些SG函数的值

SG(1): 0 SG(2): 0 SG(3): 1
SG(4): 0 SG(5): 1 SG(6): 2
SG(7): 0 SG(8): 1 SG(9): 3
SG(10): 2 SG(11): 0 SG(12): 4
SG(13): 1 SG(14): 3 SG(15): 5
SG(16): 2 SG(17): 0 SG(18): 6
SG(19): 4 SG(20): 1 SG(21): 7
SG(22): 3 SG(23): 5 SG(24): 8
SG(25): 2 SG(26): 0 SG(27): 9
SG(28): 6 SG(29): 4 SG(30): 10
SG(31): 1 SG(32): 7 SG(33): 11
SG(34): 3 SG(35): 5 SG(36): 12
SG(37): 8 SG(38): 2 SG(39): 13
SG(40): 0 SG(41): 9 SG(42): 14
SG(43): 6 SG(44): 4 SG(45): 15
SG(46): 10 SG(47): 1 SG(48): 16
SG(49): 7 SG(50): 11 SG(51): 17

不难发现一些规律

\[\operatorname{SG}(n) = \begin{cases} 0 & 0 \le n < k \\ \frac{n}{k} & n \bmod k = 0 \\ \displaystyle \operatorname{SG} \!\left(n - \!\left\lfloor \frac{n}{k} \right\rfloor\! - 1 \right) & n \bmod k !\neq 0 \end{cases} \]

我们发现直接那这个式子来暴力算复杂度还是\(O(n)\)的,考虑在转移的过程中把\(\left\lfloor \frac{n}{k} \right\rfloor\)相等的转移一起做;

\(x=\lfloor \frac{n}{k} \rfloor\),则我们需要找到一个最大的\(y\)满足\(n-y(x+1)\geq x\times k\),即\(y=\lfloor \frac{n-x\times k}{x+1}\rfloor\),这中间的转移\(\lfloor \frac{n}{k} \rfloor\)是不变的,于是我们可以直接令\({\rm SG}(n)={\rm SG}(n-(x+1)y)\)

考虑复杂度,当\(k\leq \sqrt{n}\)时,\(\left\lfloor \frac{n}{k} \right\rfloor>\sqrt{n}\),于是每次\(n\)都会至少减去\(\sqrt{n}\),于是这边复杂度是\(O(\sqrt{n})\)的;当\(k>\sqrt{n}\)时,\(\left\lfloor \frac{n}{k} \right\rfloor\leq \sqrt{n}\),按照上面的转移方式每进行一次转移\(\left\lfloor \frac{n}{k} \right\rfloor\)的值至少会减少\(1\),于是复杂度也是\(O(\sqrt{n})\)的。

代码

#include<bits/stdc++.h>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int T,n,m;
int bf(int n) {
	if(n<m) return 0;if(n%m==0) return n/m;
	return bf(n-n/m-1);
}
int sg(int n) {
	if(n<m) return 0;if(n%m==0) return n/m;
	int x=n/m;int u=x*m;
	int h=(n-u)/(x+1);
	if(n-h*(x+1)==u) return x;
	return sg(n-(h+1)*(x+1));
}
int main() {
	int ans=0;
	for(re int T=read();T;--T) {
		n=read(),m=read();
		if(m<=n/m) ans^=bf(n);
		else ans^=sg(n);
	}
	if(ans) puts("Takahashi");
	else puts("Aoki");
	return 0;
}
posted @ 2020-02-20 08:37  asuldb  阅读(242)  评论(0编辑  收藏  举报