UVA 11859 - Division Game

看题传送门

题目大意

有一个n * m的矩阵,每个元素均为2~10000之间的正整数,两个游戏者轮流操作。每次可选一行中的1个或者多个大于1的整数把它们中的每个数都变成它的某个真因子,比如12可以变成1,2,3,4,5.不能操作的输,也就是说,谁在操作之前,矩阵中的所有数是1,则输。题目要求判断第一个人是否能获胜。


初试博弈论。觉得挺好玩的~^ ^

本题可转化为Nim游戏。

题目要求让一个数变为它的真因子,等价于拿掉一个或者多个它的素因子。(12拿掉素因子2变为6,拿掉素因子3变为4,拿掉两个素因子2为3)这样,就可以每行看做一个火柴堆,每个数的素因子看成火柴。

根据 Bouton定理,状态(x1,x2,x3)为必败状态当且仅当x1 xor x2 xor x3 =0(xor为异或操作)所以我们只需要统计每一行的素因子个数,求和,异或一下判断是否为0即可。

 

怎么求素因子呢?

可以仿造用Eratosthenes快速构造素数表,

Eratosthenes构造素数表是筛选掉不超过N的每个数的整倍数。如筛选2的时候2*2,2*3,2*4.。。。详见http://blog.csdn.net/murmured/article/details/9400845

OK,这里的话,我们也从2开始,每次筛选素数的倍数。(记得得一次清干净,比如12要干掉2次2)详见代码。。


#include<cstdio>
const int MAXN=10000+2;
int cnt_primer[MAXN];
void primer()
{
	int t;
	for (int i = 2; i < MAXN; i++) 
		if(!cnt_primer[i])            //如果不为0,说明不是素数。
		{
			t=i;
			while(t < MAXN)
			{
				for(int j=t;j < MAXN;j+=t)
					cnt_primer[j]++;
				t*=i;
			}			
		}
}

int main()
{
	primer();
	int T,n,m,temp,ans,sum;
	scanf("%d",&T);
	for(int ri=1;ri <= T;ri++)
	{
		scanf("%d%d",&n,&m);
		ans=0;
		for(int i=0;i<n;i++)
		{
			sum=0;
			for(int j=0;j<m;j++)
			{
				scanf("%d",&temp);
				sum+= cnt_primer[temp];
			}
			ans^=sum;
		}
		printf("Case #%d: %s\n",ri,ans==0?"NO":"YES");

	}

}


你可能感兴趣的:(UVA 11859 - Division Game)