Live2D

Solution -「ARC 124E」Pass to Next

Description

  Link.

  有 n 个人站成一个环,初始时第 i 个人手里有 ai 个球。第 i 个人可以将自己手中任意数量的求给第 i+1 个人,第 n 个人则可以给第 1 个人。设所有人同时进行一次传球后,第 i 个人手里有 bi 个球,并令 B 为所有可能的 bn 构成的集合,求

bnBxbnx(mod998244353).

  n105

Solution

  首先有一个明显而重要的结论:存在某个人没有给别人球的传球方案一一对应了所有可能的 bn

  方法一 从暴力 DP 入手。枚举最小的 i=x,使得 x 没有向 x+1 传球,此时令 f(i,j) 表示第 i 个人给出 j 个球时,x+1i 这一段人的 b 值的乘积之和。转移时记得讨论当前的 i 能否不给下一个人球。

  写出转移式,似乎很难优化。这时不妨想一想,DP 欲求得的目标是什么?f(i,j)?并不,应是 jf(i,j)。回头看一眼转移,发现我们能直接将转移施加在 jf(i,j) 上,状态中的 j 根本不需要!

  下一步优化比较简单:两种情况的转移均能写成 2×2 的矩阵,求一求前缀积之类的就内求出答案了。复杂度 O(n)


  方法二 从组合意义入手。bi 即传完求后,每个人再从手里的球里选出一个的方案数。那么令 f(i,0/1) 表示考虑了前 i 个人,第 i 个人选出的球来自前一个人/自己,讨论转移。通过钦定第 1 个人所选出的球破环求解。复杂度 O(n)。(我没实现所以讲得比较糊 www。

Code

  只有方法一的代码呢。

/*-Rainybunny-*/

#include <bits/stdc++.h>

#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 )

const int MAXN = 1e6, MOD = 998244353, INV2 = 499122177, INV3 = 332748118;
int n, a[MAXN + 5];

inline int mul( const int u, const int v ) { return 1ll * u * v % MOD; }
inline int sub( int u, const int v ) { return ( u -= v ) < 0 ? u + MOD : u; }
inline int add( int u, const int v ) { return ( u += v ) < MOD ? u : u - MOD; }

struct Matrix {
    int a, b, c, d;
    friend inline Matrix operator * ( const Matrix& u, const Matrix& v ) {
        return { add( mul( u.a, v.a ), mul( u.b, v.c ) ),
          add( mul( u.a, v.b ), mul( u.b, v.d ) ),
          add( mul( u.c, v.a ), mul( u.d, v.c ) ),
          add( mul( u.c, v.b ), mul( u.d, v.d ) ) };
    }
} pre[MAXN + 5], suf[MAXN + 5];

inline Matrix getTrans( const int i, const bool type ) {
    int tmp = mul( mul( a[i], a[i] + 1 ), INV2 );
    if ( !type ) {
        return { tmp, a[i] + 1,
          mul( sub( a[i], mul( add( a[i], a[i] ) + 1, INV3 ) ), tmp ), tmp };
    } else {
        return { sub( mul( a[i], a[i] ), tmp ), a[i],
          mul( sub( a[i], mul( add( a[i], a[i] ) + 1, INV3 ) ), tmp ), tmp };
    }
}

int main() {
    // freopen( "y.in", "r", stdin );
    // freopen( "y.out", "w", stdout );
    std::ios::sync_with_stdio( false ), std::cin.tie( 0 );

    std::cin >> n;
    rep ( i, 1, n ) std::cin >> a[i];

    pre[0] = { 1, 0, 0, 1 };
    rep ( i, 1, n ) pre[i] = getTrans( i, 1 ) * pre[i - 1];
    suf[n + 1] = { 1, 0, 0, 1 };
    per ( i, n, 1 ) suf[i] = suf[i + 1] * getTrans( i, 0 );

    int ans = 0;
    rep ( i, 1, n ) { // i won't give i+1 any ball.
        Matrix&& tmp( pre[i - 1] * suf[i + 1] );
        ans = add( ans, add( mul( a[i], tmp.a ), tmp.c ) );
    }
    std::cout << ans << '\n';
    return 0;
}

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