我是靠谱客的博主 安详长颈鹿,最近开发中收集的这篇文章主要介绍【agc010D】Decrementing,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Portal --> agc010D

Solution

  “emmm冷静思考一波如果序列有一个(1)我就会做”

  然后冷静思考了不知道多久一点进展都没有qwq非常果断地忽略掉了一个关键点

  

  首先个人认为这题的一个突破口在于想到从奇偶性去分析(难得没有想错感天动地QwQ然后还是没有想出来),这个想法是怎么来的呢。。其实可能是因为考虑到如果说序列中有一个(1)的话,那么结果就是定的了(因为gcd永远都是(1)),然后这个时候答案只跟(sum-n)的奇偶性相关(实际上更加直接一点的式子表达应该是(sumlimits_{i=1}^{n}a[i]-1[a[i]>1]),但是因为(a[i]==1)的时候减一就是(0)了没有任何关系,所以可以直接看成(sumlimits_{i=1}^n a[i]-1=sum-n)),如果说(sum-n)为奇数则先手必胜,否则后手必胜

  接下来就是另一个突破口:注意到题目说保证初始的时候(gcd)(1),这个条件其实说明了另一个事情,就是一定存在至少一个奇数(感觉是很重要的一个点qwq我就是死活都没想到这个qwq),这个时候正解就可以十分玄学地想到要从奇数和偶数的个数奇偶性上去分类讨论了qwq

  在分类讨论前先考虑这样一个事情:因为答案与(sum-n)的奇偶性相关,而我们的操作中能够改变(sum)的奇偶性的只有两种:第一个是(-1)操作,第二个是当(gcd)为偶数的时候将这个(gcd)除掉

  然后我们开始分类讨论:

(1)如果偶数有奇数个:因为(gcd)(1),所以至少有一个奇数,这个时候我们考虑先手的一次操作,先手可以选择将一个偶数减一变成奇数,这个时候的局面是有至少两个奇数,并且有偶数个偶数,那么在接下来的操作中,无论后手怎么操作,先手总能保证到任何局面下都至少有一个奇数,也就是说(gcd)不可能为偶数,也就是说任何局面下的(sum)奇偶性都一致,那么这个时候我们算一下发现(sumlimits_{i=1}^{n}a[i]-1)必定是一个奇数(分开算奇数和偶数的贡献就好了:偶数减一之后是奇数,然后有奇数个,所以和是奇数;奇数减一之后是偶数,不管有奇数个还是偶数个,和都是偶数,最后奇数与偶数的和是奇数),所以这个时候是先手必胜
  

(2)如果偶数有偶数个:这里乍一看好像没有上面的情况那么直观好想,所以我们可以先想当只有(1)个奇数的时候的情况:如果说只有(1)个奇数,我们考虑先手的操作,如果说先手对一个偶数进行操作,因为有至少一个奇数所以除以(gcd)并不能改变奇偶性,那么接下来后手面对的局面就是((1))中的情况,然后后手就稳了,所以先手只能对唯一的那个奇数进行操作,既然先手的操作确定了,当前情况又需要根据后续的局面判断,所以我们递归处理,因为每次除(gcd),所以递归层数是(log)

  如果说有大于(1)个奇数,那么根据上面的分析显然我们不能对一个偶数操作,那如果说对奇数操作的话,因为有大于(1)个奇数,所以同样会让后手面对(1)的局面,所以这个时候无论怎么样先手都是凉凉

  

  然后就十分愉快地解决了这个问题啦ovo 代码简短解法自然

  
  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e5+10;
int a[N],cnt[2];
int n,sum;
int get_gcd(int x,int y){return y?get_gcd(y,x%y):x;}
bool solve(){
    cnt[0]=0; cnt[1]=0;
    for (int i=1;i<=n;++i)
        ++cnt[a[i]&1];
    if (cnt[0]&1) return true;
    if (cnt[1]>1) return false;
    if (a[1]==1) return false;
    int gcd=(a[1]-=(a[1]&1));
    for (int i=2;i<=n;++i){
        if (a[i]==1) return false;
        gcd=get_gcd(gcd,(a[i]-=(a[i]&1)));
    }
    for (int i=1;i<=n;++i) a[i]/=gcd;
    return solve()^1;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
#endif
    scanf("%d",&n);
    for (int i=1;i<=n;++i)
        scanf("%d",a+i);
    if (solve()) printf("Firstn");
    else printf("Secondn");
}

转载于:https://www.cnblogs.com/yoyoball/p/9629933.html

最后

以上就是安详长颈鹿为你收集整理的【agc010D】Decrementing的全部内容,希望文章能够帮你解决【agc010D】Decrementing所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(72)

评论列表共有 0 条评论

立即
投稿
返回
顶部