【Atcoder Grand Contest 010】D

题目链接

题目大意:给定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 }
agc010 D
原文地址:https://www.cnblogs.com/JKAI/p/7599877.html