Live2D

Solution -「多校联训」取石子游戏

Description

  Link.

  有 n 堆石子,第 i 堆有 xi 个,Alice 每次只能从这堆中拿走 ai 个石子,Bob 每次只能从这堆中拿走 bi 个石子,不能操作者负。对于 i=1,2,,n,求只考虑 [1,i] 的石子堆时,双方博弈的结果(有 Alice 必胜、Bob 必胜、先手必胜、后手必胜四种结果)。

  n105

Solution

  我不会博弈啊……

  在这种非对称博弈问题中,一般不去研究 SG 函数,而是考虑“Alice 能比 Bob 多操作多少次”一类的问题。我们来逐步分析本题:

  结论:双方可以通过轮流操作同一堆石子,直至不存在这样石子堆,来达到最优策略。如果一方需要单独操作某一堆,另一方必然可以缠着(?)她,所以这个结论比较自然。

  所以只用考虑 ri=ximod(ai+bi) 的取值

  • ri<min{ai,bi}:双方无法操作,忽略;
  • min{ai,bi}<ri<max{ai,bi}:只有一方能操作,计入“能多操作几次”的贡献;
  • max{ai,bi}ri:双方都能操作,但一方操作后另一方不能操作。

  先将所有第三类的操作记给 Alice,那么 Bob 选择一个 ri 会使得 Alice 与 Bob 的操作次数差减少 airi+biri,可见双方都会从大到小选择这一值,线段树维护选择结果即可。复杂度 O(nlogn)

Code

/*~Rainybunny~*/

#include <cstdio>

#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )

typedef long long LL;

const int MAXN = 1e5;
int n, x[MAXN + 5], a[MAXN + 5], b[MAXN + 5];

struct SegmentTree {
    static const int MAXND = 4e6;
    int node, ch[MAXND][2], cnt[MAXND];
    LL sum[MAXND][2];

    inline void pushup( const int u ) {
        cnt[u] = cnt[ch[u][0]] + cnt[ch[u][1]];
        sum[u][0] = sum[ch[u][1]][0] + sum[ch[u][0]][cnt[ch[u][1]] & 1];
        sum[u][1] = sum[ch[u][1]][1] + sum[ch[u][0]][!( cnt[ch[u][1]] & 1 )];
    }

    inline void update( int& u, const int l, const int r, const int x ) {
        if ( !u ) u = ++node;
        if ( l == r ) {
            ++cnt[u];
            sum[u][0] = ( cnt[u] + 1ll >> 1 ) * l;
            sum[u][1] = 1ll * ( cnt[u] >> 1 ) * l;
            return ;
        }
        int mid = l + r >> 1;
        if ( x <= mid ) update( ch[u][0], l, mid, x );
        else update( ch[u][1], mid + 1, r, x );
        pushup( u );
    }
} sgt;

int main() {
    freopen( "C.in", "r", stdin );
    freopen( "C.out", "w", stdout );

    scanf( "%d", &n );
    rep ( i, 1, n ) scanf( "%d %d %d", &x[i], &a[i], &b[i] );
    
    LL dif = 0; int rt = 0;
    rep ( i, 1, n ) {
        int r = x[i] % ( a[i] + b[i] );
        if ( r >= a[i] && r >= b[i] ) {
            dif += r / a[i], sgt.update( rt, 1, 2e9, r / a[i] + r / b[i] );
        } else if ( r >= a[i] || r >= b[i] ) {
            dif += ( r >= a[i] ? 1 : -1 ) * ( r / a[i] + r / b[i] );
        }

        bool aw = dif - sgt.sum[rt][1] > 0, bw = dif - sgt.sum[rt][0] < 0;
        if ( !aw && !bw ) puts( "Second" );
        else if ( !aw ) puts( "Bob" );
        else if ( !bw ) puts( "Alice" );
        else puts( "First" );
    }
    return 0;
}

posted @   Rainybunny  阅读(66)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示