【Codeforces Round 334 (Div 2)E】【博弈-SG函数】Lieges of Legendre n个数 每次取数-1或者变2x为k个x的博弈

 Lieges of Legendre
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Kevin and Nicky Sun have invented a new game called Lieges of Legendre. In this game, two players take turns modifying the game state with Kevin moving first. Initially, the game is set up so that there are n piles of cows, with the i-th pile containing ai cows. During each player's turn, that player calls upon the power of Sunlight, and uses it to either:

  1. Remove a single cow from a chosen non-empty pile.
  2. Choose a pile of cows with even size x (x > 0), and replace it with k piles of x cows each.

The player who removes the last cow wins. Given nk, and a sequence a1, a2, ..., an, help Kevin and Nicky find the winner, given that both sides play in optimal way.

Input

The first line of the input contains two space-separated integers n and k (1 ≤ n ≤ 100 000, 1 ≤ k ≤ 109).

The second line contains n integers, a1, a2, ... an (1 ≤ ai ≤ 109) describing the initial state of the game.

Output

Output the name of the winning player, either "Kevin" or "Nicky" (without quotes).

Sample test(s)
input
2 1
3 4
output
Kevin
input
1 2
3
output
Nicky
Note

In the second sample, Nicky can win in the following way: Kevin moves first and is forced to remove a cow, so the pile contains two cows after his move. Next, Nicky replaces this pile of size 2 with two piles of size 1. So the game state is now two piles of size 1. Kevin then removes one of the remaining cows and Nicky wins by removing the other.



#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}
const int N=0,M=0,Z=1e9+7,ms63=1061109567;
int n,k,x;
int odd(int x)
{
	if(x==0)return 0;
	if(x==1)return 1;
	if(x==2)return 0;
	if(x>=5&&(x&1))return 0;
	int two=0;while(x%2==0){x/=2;++two;}
	if(x==3)return two&1?2:1;
	else return two&1?1:2;
}
int even(int x)
{
	if(x<=2)return x;
	else return (x&1)?0:1;
}
void oddtable()
{
	int sg[105];
	bool e[105];
	sg[0]=0;
	for(int i=1;i<=100;++i)
	{
		MS(e,1);
		e[sg[i-1]]=0;
		if(i%2==0)e[sg[i/2]]=0;
		int j;for(j=0;;++j)if(e[j])break;
		sg[i]=j;
	}
	for(int i=1;i<=100;++i)
	{
		printf("%d %d %d\n",i,sg[i],odd(i));
	}
}
int main()
{
	//oddtable();
	while(~scanf("%d%d",&n,&k))
	{
		int ans=0;
		for(int i=1;i<=n;++i)
		{
			scanf("%d",&x);
			if(k&1)ans^=odd(x);
			else ans^=even(x);
		}
		puts(ans?"Kevin":"Nicky");
	}
	return 0;
}
/*
【题意】
有n(1<=n<=1e5)个数值a[],每个数值的范围都在[1,1e9]范围。
Kevin先手,Nicky后手。当一个人再也无法操作的时候认为这个人败。
操作有两种——
1,选择一个数,做-1操作,数如果变成0,就移除。
2,选择一个偶数,设这个偶数为2x,我们把这个数移除,并替换成k个x

【类型】
博弈

【分析】
好久没有遇到正儿八经的博弈题了,遇到之后发现自己什么都不会TwT。
那么——这道题要如何做呢?
首先,要指出我做博弈题的一个傻叉点。
我思考的时候,每个数,要不就是必胜态,要不就是必败态。
我忽略了——事实上,每个数,是可能对应着一个SG值的。
我们最后的胜负,不是由于必胜态或必败态叠加决定。
而是经过若干SG值的异或结果决定。
SG值表示,对于一个状态,如果它的前驱节点有,有某个SG值,这个状态就不能取这个SG值。
对于这个状态,从0开始,它可以取的最小的数字,就作为它的SG值。
如果SG值的异或和为0,那么先手必败;否则先手必胜。

然而,我们要如何算出每个数的SG值呢?
SG值一般的计算方法可以是DP打表,然而这道题的数值范围巨大, 显然没法打表,我们要更一针见血一些。

首先,我们可以显然得到一个结论,k影响我们答案的,实际只是其奇偶性。

因为,如果k为偶数,那么对于操作2,我们会分裂出偶数个f(x),而这偶数个f(x)的异或和显然是0,这个点就可以达到一个SG值为0的前驱
所以,在k为偶数的情况下,我们可以算出——
f[0]=0;
f[1]=1;
f[2]=2;
f[3]=0;
f[4]=1;
接下来呢?
f[5]=0;
f[6]=1;
f[7]=0;
f[8]=1;
显然奇数只能达到偶数前驱,偶数前驱的SG值都为0,所以这些奇数的SG值都为1。
显然偶数能达到奇数前驱,奇数前驱的SG值都为0,而它能分裂到达的SG值也为0,所以偶数的SG值也都为1.
于是我们就求出所有点的SG值啦。

再讨论,如果k为奇数,那么对于操作2,我们会分裂出奇数个f(x),而这奇数个f(x)的异或值相消,就只剩下了一个f(x)
所以,在k为奇数的情况下,我们可以算出——
sg[0]=0;
sg[1]=1;
sg[2]=0;
sg[3]=1;
sg[4]=2;
接下来呢?
发现情况有些复杂,于是我们可以打个表——
sg[5]=0;
sg[6]=2;
sg[7]=0;
sg[8]=1;
sg[9]=0;
sg[10]=1;
sg[11]=0;
sg[12]=1;
sg[13]=0;
sg[14]=1;
sg[15]=0;
sg[16]=2;
sg[17]=0;
sg[18]=1;
sg[19]=0;
sg[20]=2;
int odd(int x)
{
	if(x==0)return 0;
	if(x==1)return 1;
	if(x==2)return 0;
	if(x>=5&&(x&1))return 0;
	int two=0;while(x%2==0){x/=2;++two;}
	if(x==3)return two&1?2:1;
	else return two&1?1:2;
}
我们会发现,除了1和3的SG值为1,其他奇数的SG值全是0。
偶数(设这个偶数为X为2x)的SG值呢?首先不会是0.然后不停地迭代下去——
	如果SG[x]==1,那么SG[2x]就是2
	如果SG[x]==2,那么SG[2x]就是1
于是,我们要看X中有多少个2,设X=2^k*x,
sg[1]=1;
sg[3]=1;
sg[other odd]=0;
先讨论,如果x>=5且x为奇数,即sg[other odd]==0的情况——
	如果k为奇数,那么sg[X]=1;
	如果k为偶数,那么sg[X]=2;
再讨论,如果x==3,即sg[3]=1的情况,
	如果k为奇数,那么sg[X]=2;
	如果k为偶数,那么sg[X]=1;
再讨论,如果x==1,即sg[1]==1的情况
	sg[1]要特判为1,sg[2]要特判为0,其他情况,与x>=5且x为奇数的情况相同。
于是,这些写下之后这样这道题就做完了!

【时间复杂度&&优化】
if(x&1)—— O(nlogMAX)
else —— O(n)

【数据】
1 3
4

*/


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值