我是靠谱客的博主 眼睛大小鸽子,最近开发中收集的这篇文章主要介绍AtCoder 2305 [AGC010D] Decrementing(博弈)problemsolution,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

problem

Luogu链接

solution

case1 a a a 中如果有 1 1 1 的存在,那么相当于第二步失效,最后的结果仅由 ∑ a i − 1 sum a_i-1 ai1 的奇偶性决定(奇数先手赢,偶数后手赢)

奇数先手赢,偶数后手赢。

case2:如果 ∑ a i − 1 sum a_i-1 ai1 为奇数,先手必胜。

这看起来就似乎是正确的。

首先因为偶数除以奇数还是偶数,奇数除以奇数还是奇数,所以如果 gcd ⁡ gcd gcd 是奇数,对奇偶性不影响。

如果先手能维持这个奇偶性到最后,即保证每人每次只有第一步操作有效,那他就赢了。

先手只需任选一个偶数 − 1 -1 1,再算上初始局面必然包含的一个奇数(否则初始 gcd ⁡ gcd gcd 不为 1 1 1),这样至少就有两个奇数。

  • 此时若序列长度 > 2 >2 >2,先手 − 1 -1 1 之后:
    • gcd ⁡ = 1 gcd=1 gcd=1。后手再让任意一个数 − 1 -1 1,序列的 gcd ⁡ gcd gcd 仍然为 1 1 1,后手的第二步等价于无效。
    • gcd ⁡ ≠ 1 gcdne 1 gcd=1,但 gcd ⁡ gcd gcd 也一定是奇数。除掉 gcd ⁡ gcd gcd 后,出现 1 1 1,奇偶性未改变。后手仍然面临的奇偶性为偶。先手必胜。
  • 特殊情况是序列长度 = 2 =2 =2,先手 − 1 -1 1 之后:
    • 可能两数相等,那正好就直接赢了。
    • 要是不相等,并且 − 1 -1 1 之后出现了倍数关系,那没什么影响,显然一个是另一个的奇数倍。约去 gcd ⁡ gcd gcd 后就是一个 1 1 1 加上一个奇数,后手面临的奇偶性仍然为偶,先手仍然获胜。

要想保证每人每次只能取一个也很简单,先手只需任选一个偶数减1,再算上初始局面必然包含的一个奇数(否则初始gcd不为1),这样就至少有两个奇数了。

case3:如果 ∑ i = 1 n a i − 1 sum_{i=1}^na_i-1 i=1nai1 为偶数,且有 > 1 >1 >1 a i a_i ai 为奇数,则先手必败。

若有多个奇数,这一轮先手选择一个变为偶数, gcd ⁡ gcd gcd 仍然只可能是奇数,下一轮后手就能将其重新变回偶数,使先手第二步无效。

case4:如果为偶数,先手肯定想要翻盘,唯一办法是 2 ∣ gcd ⁡ 2mid gcd 2gcd

如果 ∑ i = 1 n a i − 1 sum_{i=1}^na_i-1 i=1nai1 为偶数,且只有一个奇数,先手一定操作这个奇数,那么这一轮至少能 / 2 /2 /2,这是唯一的翻盘机会。

考虑做完上述特判和操作后,将操作权递交给对手。

只有 case4 是需要模拟操作下去的 O ( n log ⁡ 2 w ) O(nlog^2w) O(nlog2w),其余三种情况一步判断输赢即可。

#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
#define int long long
int n;
int a[maxn];

int gcd( int x, int y ) {
    if( ! y ) return x;
    else return gcd( y, x % y );
}

signed main() {
    scanf( "%lld", &n );
    for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] );
    bool op = 0;
    while( 1 ) {
        int cnt = 0, sum = 0, flag = 0;
        for( int i = 1;i <= n;i ++ )
            cnt += (a[i] & 1), sum += a[i] - 1, flag |= (a[i] == 1);
        if( sum & 1 ) break;
        if( cnt ^ 1 ) { op ^= 1; break; }
        if( flag ) { op ^= 1; break; }
        int d = 0;
        for( int i = 1;i <= n;i ++ ) {
            if( a[i] & 1 ) a[i] --;
            d = gcd( d, a[i] );
        }
        for( int i = 1;i <= n;i ++ ) a[i] /= d;
        op ^= 1;
    }
    if( ! op ) puts("First");
    else puts("Second");
    return 0;
}

最后

以上就是眼睛大小鸽子为你收集整理的AtCoder 2305 [AGC010D] Decrementing(博弈)problemsolution的全部内容,希望文章能够帮你解决AtCoder 2305 [AGC010D] Decrementing(博弈)problemsolution所遇到的程序开发问题。

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

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

评论列表共有 0 条评论

立即
投稿
返回
顶部