【Atcoder Grand Contest 010】D - Decrementing——博弈论
题目大意:给定n个最大公约数为1的整数,两个人轮流进行操作,每次操作可以选一个大于1的数使其减1,然后所有的数再除以当前的最大公约数(如3 6 10对10操作后得到1 2 3),当其中一个人无法操作时,输掉比赛,求获胜的是先手还是后手。先手输出First,后手输出Second。
分析:
我们先想一下,如何能使整段序列变成全为1,很显然只有当序列变为形如k,k,k,k,k+1才能一步变为全为1的序列。那么另一个人一定会避免让对手拿到这样的序列(可以通过操作别的数使得不会出现这种情况),当且仅当k=1时,才能决出胜负。
也就是说,*获胜的一方一定会拿到形如1,1,1,1,2的序列(性质1)。
一、当偶数的个数为奇数时:
(1)当n>2时,先手可以通过选数来使得gcd一直为1。如果此时gcd一直为1会发生什么呢?
因为两人各选一次后偶数个数奇偶性不变,那么先手是一定获胜的(参考性质1)。
(2)当n=2时,先手可能无法避免出现除法(会出现两数同为奇数的情况),此时会出现除以奇数的情况。
奇数除以奇数还是奇数,因此 奇偶性依然不会变,所以先手依然必胜。
*当偶数的个数为奇数时,先手必胜(性质2)。
二、当偶数的个数为偶数时,先手要利用先手的优势尝试扭转偶数个数的奇偶性:唯一扭转个数奇偶性的方法就是同除一个偶数,这样的话偶数可能变成奇数,当然也可能还是偶数。
(1)*当奇数个数大于1或奇数中存在1时,无法逆转,先手必败(性质3)。
(2)当奇数个数为1且该奇数不为1时,递归模拟,当出现操作后偶数个数为奇数的时候,
说明此操作的执行者必败(参考性质2);当序列中出现1或奇数个数大于1时,逆转不可能再发生了,可以直接得出答案。
*当偶数个数为偶数,奇数个数为1且此奇数不为1时,先手有可能获胜,也有可能输掉比赛(性质4)。
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 const int N=1e5+10; 5 int a[N],ma=0,n; 6 int read(){ 7 int ans=0,f=1;char c=getchar(); 8 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 9 while(c>='0'&&c<='9'){ans=ans*10+c-48;c=getchar();} 10 return ans*f; 11 } 12 /*-----------------------------------------------------*/ 13 int gcd(int x,int y){return !y?x:gcd(y,x%y);} 14 bool dfs(int p){ 15 int mc=0,flag=1; 16 for(int i=1;i<=n;i++)if(a[i]%2){a[i]--;break;} 17 int gc=a[1]; 18 for(int i=2;i<=n;i++)gc=gcd(gc,a[i]); 19 for(int i=1;i<=n;i++){a[i]/=gc;if(!(a[i]%2))mc++;if(a[i]==1)flag=0;} 20 if(mc%2)return !p; 21 if(!flag||n-mc!=1)return p; 22 return dfs(p^1); 23 } 24 int main(){ 25 n=read();int mc=0;bool flag=1; 26 for(int i=1;i<=n;i++){ 27 a[i]=read();if(a[i]%2==0)mc++; 28 else { 29 ma++;if(a[i]==1)flag=0; 30 } 31 } 32 if(mc%2)printf("First"); 33 else { 34 if(ma!=1||!flag)printf("Second"); 35 else if(dfs(1))printf("First"); 36 else printf("Second"); 37 } 38 return 0; 39 }