Live2D

Solution -「Gym 102956B」Beautiful Sequence Unraveling

Description

  Link.

  求长度为 n,值域为 [1,m] 的整数序列 an 的个数,满足 i[1,n), maxj=1i{aj}=minj=i+1n{aj},答案对大素数 p 取模。

  n400m108

Solution

  前几天刚胡了一个 "DP and DP" 的 trick,这不又遇见一道。(

  注意到 m 很大,一副拒 DP 状态于千里之外的样子,尝试先回避它——将某个合法 an 离散化,此时值域不超过 [1,n]。但“离散化”又要求值域中每个位置出现,那我们折中:只要求值域为 [1,n],而不限制每个位置出现。可是为了将值域还原为 [1,m],我们又势必得知“有多少个合法 an,其中 ax 种不同取值”。

  一步步来,先考虑求出 f(i,j),表示长度为 i 的序列,值域在 [1,j] 时的合法数量。总数减去不合法数,枚举最后一个不合法位置 k1,以及此时前缀 max 和后缀 min 的值 t,可以发现 ak..n 构成子问题,得到转移

f(i,j)=jik=2it=1j(tk1(t1)k1)(f(ik+1,jt+1)f(ik+1,jt)).

合理处理系数的指标和转移状态的下标,可以利用前缀和将求 f 的过程优化至 O(n3)

  如刚才透露的一般,我们在目标状态 f(n,1..n) 的基础下求“有 x 个不同取值”。令 g(i) 表示长度为 n 的序列中,离散化后满足值域恰为 [1,i] 的合法数量,简单地

g(i)=f(n,i)j=1i1(ij)g(j).

  答案变得显而易见

answer=i=1n(mi)gi.

其中组合数展开成下降幂暴力算即可。总复杂度 O(n3)

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 )

const int MAXN = 400;
int n, m, P, fac[MAXN + 5], ifac[MAXN + 5];
int pwr[MAXN + 5][MAXN + 5], ipwr[MAXN + 5][MAXN + 5];
int f[MAXN + 5][MAXN + 5], g[MAXN + 5];

inline int mul( const int a, const int b ) { return 1ll * a * b % P; }
inline void subeq( int& a, const int b ) { ( a -= b ) < 0 && ( a += P ); }
inline int add( int a, const int b ) { return ( a += b ) < P ? a : a - P; }
inline void addeq( int& a, const int b ) { ( a += b ) >= P && ( a -= P ); }
inline int sub( int a, const int b ) { return ( a -= b ) < 0 ? a + P : a; }
inline int mpow( int a, int b ) {
    int ret = 1;
    for ( ; b; a = mul( a, a ), b >>= 1 ) ret = mul( ret, b & 1 ? a : 1 );
    return ret;
}

inline void init() {
    fac[0] = 1;
    rep ( i, 1, n ) fac[i] = mul( i, fac[i - 1] );
    ifac[n] = mpow( fac[n], P - 2 );
    per ( i, n - 1, 0 ) ifac[i] = mul( i + 1, ifac[i + 1] );

    rep ( i, 0, n ) {
        pwr[i][0] = ipwr[i][0] = 1;
        ipwr[i][1] = mpow( pwr[i][1] = i, P - 2 );
        rep ( j, 2, n ) {
            pwr[i][j] = mul( i, pwr[i][j - 1] );
            ipwr[i][j] = mul( ipwr[i][j - 1], ipwr[i][1] );
        }
    }
}

inline int comb( const int a, const int b ) {
    return a < b ? 0 : mul( fac[a], mul( ifac[b], ifac[a - b] ) );
}

int main() {
    scanf( "%d %d %d", &n, &m, &P ), init();

    rep ( j, 1, n ) {
        static int sum[MAXN + 5][2];
        rep ( i, 1, n ) sum[i][0] = sum[i][1] = 0;
        rep ( i, 1, n ) {
            int& cur = f[i][j] = pwr[j][i];
            rep ( t, 1, j ) {
                subeq( cur, mul( pwr[t][i], sum[t][0] ) );
                addeq( cur, mul( pwr[t - 1][i], sum[t][1] ) );
            }
            rep ( t, 1, j ) {
                addeq( sum[t][0], mul( ipwr[t][i],
                  sub( f[i][j - t + 1], f[i][j - t] ) ) );
                addeq( sum[t][1], mul( ipwr[t - 1][i],
                  sub( f[i][j - t + 1], f[i][j - t] ) ) );
            }
        }
    }

    int ans = 0, c = 1;
    rep ( i, 1, n ) {
        int& cur = g[i] = f[n][i];
        rep ( j, 1, i - 1 ) subeq( cur, mul( comb( i, j ), g[j] ) );
        c = mul( c, mul( m - i + 1, ipwr[i][1] ) );
        addeq( ans, mul( c, g[i] ) );
    }
    printf( "%d\n", ans );
    return 0;
}

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