HDU - 4388 Stone Game II(博弈+思维)

题目链接:点击查看

题目大意:最初有n堆石子,每堆石子的数目已知,现在有两个人轮流按照下列规则操作,不能操作的一方即为失败

  1. 首先选择一堆石子,设该堆石子目前有x个,从中拿走a个石子,剩下了k个石子,a和k必须满足
    1. 0
    2. 0
    3. k^x
  2. 然后加入一堆新的石子,数目为k^x,每个人每一局可以使用一次技能,让新增加的石子为(2*k)^x

题目分析:这个题如果直接去分析挺难想的,我们可以转换一下思维模式,上述两个规则无疑就是将n堆石子中满足条件的一堆分成了两堆,其新的值为k和k^x,直到不能再分为止,能想到这里还是差点火候,下面我用网上大佬的思路,来证明一下k和k^x的关系:

我们分成四种情况,讨论其中二进制的某一位p:

x k k^x
0 0 0
0 1 1
1 0 1
1 1 0

通过上表,我们可以看出,在将x分为k和k^x后,1的个数之和的奇偶性不变,然后从某一位p的结论推广到整个x和k中,可以得到相同的结论,我也不知道是怎么想到的。。换做是我肯定想不到orz

下面在讨论一下关于终止条件,也就是必败条件是什么,我们选出其中一堆,若该堆石子的数目转换为二进制后,1的数目只有一个,那么该堆是无法再分的,因为题目要求了k^x

所以我们现在有了两个结论:

  1. x 和k与k^x中1的数目之和的奇偶性相同
  2. 每次k至少需要选择x中任意数目的1

最后再分析一下每个人可以使用的那个技能,因为是将k^x变为(2k^x),乘二在二进制里也不会影响1的数目之和的奇偶性,所以这个技能实际上没有什么贡献

综上所述,我们只需要分析一下初始时n堆石子中1的数目之和,然后减去原本n堆中需要保留的一个1(必败态),再判断一下奇偶性就能判断操作次数了,如果答案为奇数,则先手能操作最后一步,先手必胜,否则先手必败

对了,因为这个题目需要统计每个数字中1出现的次数,一般来说我们都会直接跑一遍二进制,今天学会了一个更快的方法可以优化,就是用n&(n-1),下面放上大佬的证明:

https://blog.csdn.net/u013243347/article/details/52220551

实现代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include 
using namespace std;

typedef long long LL;

typedef unsigned long long ull;

const int inf=0x3f3f3f3f;

const int N=2e5+100;

int getnum(int n)
{
	int cnt=0;
	while(n)
	{
		n&=n-1;
		cnt++;
	}
	return cnt;
}

int main()
{
//  freopen("input.txt","r",stdin);
    int w;
    cin>>w;
    int kase=0;
    while(w--)
    {
    	int n;
    	int ans=0;
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		int num;
    		scanf("%d",&num);
    		ans+=getnum(num);
		}
		printf("Case %d: %s\n",++kase,(ans-n)&1?"Yes":"No");
	}
    
    

    
    
    
    
    
    
    
    
    
    
    return 0;
}

 

你可能感兴趣的:(思维,博弈)