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"); } }
新 blog : www.hrwhisper.me