zoukankan      html  css  js  c++  java
  • 「ABC216H」Random Robots

    题目

    点这里看题目。

    分析

    尝试构建一个分层图来描述机器人的行动。抛开初始点不谈,我们可以构造出一个 (n+1) 层,每层有若干个点的图,用 ((i,j)) 表示第 (i) 层的第 (j) 个点。那么机器人的静止可以用有向边 ((i,j) ightarrow (i+1,j)) 表示;移动可以用 ((i,j) ightarrow (i+1,j+1)) 表示。重要的性质是,这是一个平面图

    加入初始层,也即新建一层,共 (K) 个点,第 (i) 个点连向 (x_i),该图的平面图性质保持不变。这样,一种机器人的移动方案就是从初始层到第 (n+1) 层的 (K) 条路径组合,对于初始层的每个点 (u) 都有且仅有一条路径起点在 (u),而我们所需要的方案数则是路径两两没有点相交的方案数。

    再次注意这是一个平面图,我们可以使用 LGV 引理计算答案。由于第 (n+1) 层大小不确定,我们可以枚举其任意一个子集,按顺序作为每条路径的终点。

    当然,这样算还是太暴力了,直接将矩阵乘起来再算行列式复杂度无法接受。由于矩阵形式简单,如果确定了起点终点,方案数可以直接用组合数算出来,所以我们可以尝试设计一个 DP 替代行列式。设 (f_{i,S}) 表示考虑了 ([0,i]) 的终点后,已经确定了起点在 (S) 中的路径的带行列式系数的和。转移枚举加入 (S) 中的元素即可。

    这样的时间复杂度即为 (O(2^kk(n+max x)))

    小细节:由于 (x) 可以为 0,所以我们需要考虑终点为 0 的情况。写刷表的时候尤其注意。

    小结:

    1. 注意将机器人移动转化为图,将机器人移动方案转化为路径组的方法

    2. 注意这种行列式的计算方式,相当于变相地枚举行列式的排列。

      这样做的合理性在于元素之间相互独立,易于计算,我们就可以跳过矩阵。

    代码

    #include <cstdio>
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    const int mod = 998244353;
    const int MAXK = 15, MAXN = 1005, MAXS = ( 1 << 10 ) + 5;
    
    template<typename _T>
    void read( _T &x )/*{{{*/
    {
        x = 0; char s = getchar(); int f = 1;
        while( ! ( '0' <= s && s <= '9' ) ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
        while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
        x *= f;
    }/*}}}*/
    
    template<typename _T>
    void write( _T x )/*{{{*/
    {
        if( x < 0 ) putchar( '-' ), x = -x;
        if( 9 < x ) write( x / 10 );
        putchar( x % 10 + '0' );
    }/*}}}*/
    
    int fac[MAXN << 1], ifac[MAXN << 1];
    
    int dp[MAXS];
    int coe[MAXS][MAXK];
    
    int X[MAXK];
    int K, N;
    
    inline int Qkpow( int, int );
    inline int Mul( int x, int v ) { return 1ll * x * v % mod; }
    inline int Inv( const int a ) { return Qkpow( a, mod - 2 ); }
    inline int Sub( int x, int v ) { return ( x -= v ) < 0 ? x + mod : x; }
    inline int Add( int x, int v ) { return ( x += v ) >= mod ? x - mod : x; }
    inline int C( int n, int m ) { return n < m ? 0 : Mul( fac[n], Mul( ifac[m], ifac[n - m] ) ); }
    inline void Upt( int &x, const int v ) { x = Add( x, v ); }
    
    inline int Qkpow( int base, int indx )/*{{{*/
    {
        int ret = 1;
        while( indx )
        {
            if( indx & 1 ) ret = Mul( ret, base );
            base = Mul( base, base ), indx >>= 1;
        }
        return ret;
    }/*}}}*/
    
    inline void Init( const int n = 2000 )/*{{{*/
    {
        fac[0] = 1; rep( i, 1, n ) fac[i] = Mul( fac[i - 1], i );
        ifac[n] = Inv( fac[n] ); per( i, n - 1, 0 ) ifac[i] = Mul( ifac[i + 1], i + 1 );
    }/*}}}*/
    
    int main()
    {
        read( K ), read( N );
        rep( i, 1, K ) read( X[i] ), X[i] ++;
        Init(), dp[0] = 1; 
        int all = ( 1 << K ) - 1;
        rep( S, 0, all )
        {
            coe[S][K] = 1;
            per( i, K - 1, 1 )
            {
                coe[S][i] = coe[S][i + 1];
                if( S >> i & 1 ) coe[S][i] = mod - coe[S][i];
            }
        }
        int div2 = Qkpow( ( mod + 1 ) / 2, N );
        rep( i, 0, X[K] + N )
            per( S, all, 0 )
        {
            if( ! dp[S] ) continue;
            rep( j, 1, K )
            {
                if( i + 1 < X[j] || i + 1 - N > X[j] || ( S >> ( j - 1 ) & 1 ) ) continue;
                Upt( dp[S | ( 1 << ( j - 1 ) )], Mul( Mul( div2, dp[S] ), Mul( coe[S][j], C( N, i + 1 - X[j] ) ) ) );
            }
        }
        write( dp[all] ), putchar( '
    ' );
        return 0;
    }
    
  • 相关阅读:
    算法
    nginx配置https
    IE中JS跳转丢失referer的问题
    js 调用字符串类型的 js语句
    移动端iOS中input聚焦不灵敏
    上传图片转换格式为base64并预览
    转:手机号脱敏
    转:Lodash之throttle(节流)与debounce(防抖)总结
    转:tinyMCE中图片的自定义上传
    chrome input 输入框去掉黄色
  • 原文地址:https://www.cnblogs.com/crashed/p/15204772.html
Copyright © 2011-2022 走看看