题解 AT2305 【[AGC010D] Decrementing】
\texttt{Soultion}
考虑什么样的局面是必胜的
容易发现是
对唯一的
然而,再思考一步,
因为
故必胜的状态应该是
首先,当序列出现1时,游戏已经结束,奇偶性就能判断
具体判断是有奇个偶元素则先手胜,反之后手胜
其他情况只需考虑偶数元素奇偶性的变化即可
下面提及的奇数指大于1的奇数
若目前有奇个偶数,显然我们是优的,只要保持这奇个偶数,最后就能取得胜利
我们只要选一个偶数减一,所有数的奇偶性不发生改变
对面怎么选都改变不了偶数元素个数的奇偶性,先手必胜
若目前有偶个偶数,我们不优,改变奇偶性的唯一办法就是令2|gcd
若有多个奇数,我们这一轮让一个变成偶数,下一轮对面就能把它成奇数,无法除2,先手必败
唯一的特例是只有一个奇数时,我们这一轮就能至少除2,这也是我们唯一的翻盘机会
考虑做完上面提及的操作,将决策扔给对手
至此,我们所有情况都讨论完了,小结下
- 若元素中出现1,判断奇偶性即可
- 若有奇个偶元素,先手必胜
- 若有偶个偶元素且奇元素数量大于1,后手必胜
- 若有偶个偶元素且奇元素数量等于1,操作唯一的奇元素后将先手权交给后手,后手继续判断
由于继续的只有 Case4,在 Case4 中元素至少全部除2
求gcd暴力求会多带一个log
复杂度
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int a[maxn];
int read(){
int x=0,y=1;
char ch=getchar();
while(ch<48||ch>57){if(ch==45)y=-1;ch=getchar();}
while(ch>=48&&ch<=57)x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*y;
}
int gcd(int x,int y){
while(y){
x%=y;
swap(x,y);
}
return x;
}
int main(){
// freopen("AT2305.in","r",stdin);
// freopen("AT2305.out","w",stdout);
int n;
n=read();
for(int i=1;i<=n;i++)a[i]=read();
int op=0;
while(1){
int c=0,d=0,p;
bool flag=0;
for(int i=1;i<=n;i++){
if(a[i]>1&&(a[i]&1)){c++;p=i;}
else if(a[i]%2==0)d++;
if(a[i]==1)flag=1;
}
if(flag){
if(d&1){
if(!op)puts("First");
else puts("Second");
}
else{
if(op)puts("First");
else puts("Second");
}
return 0;
}
if(d&1){
if(!op)puts("First");
else puts("Second");
return 0;
}
if((d%2==0)&&c>1){
if(!op)puts("Second");
else puts("First");
return 0;
}
if((d%2==0)&&c==1){
a[p]--;
int g=0;
for(int i=1;i<=n;i++)g=gcd(g,a[i]);
for(int i=1;i<=n;i++)a[i]/=g;
op^=1;
}
}
return 0;
}
本题最重要的就是想到考虑偶元素数量的奇偶性,将原本难以下手的除以gcd变成了只要考虑奇偶性的简单问题