大意: 给定$n$堆石子, 两人轮流操作, 每次操作两种选择
$(1)$任选非空堆拿走一个石子
$(2)$任选石子数为$2x(x>0)$的一堆, 替换为$k$堆$x$个石子. ($k$给定)
最后无法操作则输, 求谁能赢.
根据$SG$定理可以得到
$sg(x) = \begin{cases} mex\{sg(x-1)\},&\text{$x$为奇} \\ mex\{sg(x-1),sg(\frac{x}{2})\}, & \text{$x$为偶且$k$为奇} \\ mex\{sg(x-1),0\}, & \text{$x$为偶且$k$为偶} \end{cases}$
然后找规律, 可以发现$k$为偶数时有循环节.
$k$为奇数时, $x$为较大的奇数一定必败, 那么就可以省去计算$sg(x-1)$, 只在偶数时计算$sg(\frac{x}{2})$, 这样单次计算$sg$复杂度就为$O(logx)$
#include <iostream> #define REP(i,a,n) for(int i=a;i<=n;++i) using namespace std; int n, k; const int f[]={0,1,0,1,2,0}; int solve(int x) { if (x<=5) return f[x]; if (x&1) return 0; int t = solve(x/2); return t==1?2:1; } int sg(int x) { if (k%2==0) { if (x==1) return 1; if (x==2) return 2; return x%2==0; } return solve(x); } int main() { scanf("%d%d", &n, &k); int ans = 0; REP(i,1,n) { int t; scanf("%d", &t); ans ^= sg(t); } puts(ans?"Kevin":"Nicky"); }