点这里看题目。
分析尝试构建一个分层图来描述机器人的行动。抛开初始点不谈,我们可以构造出一个 \(n+1\) 层,每层有若干个点的图,用 \((i,j)\) 表示第 \(i\) 层的第 \(j\) 个点。那么机器人的静止可以用有向边 \((i,j)\rightarrow (i+1,j)\) 表示;移动可以用 \((i,j)\rightarrow (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 的情况。写刷表的时候尤其注意。
小结:
-
注意将机器人移动转化为图,将机器人移动方案转化为路径组的方法。
-
注意这种行列式的计算方式,相当于变相地枚举行列式的排列。
这样做的合理性在于元素之间相互独立,易于计算,我们就可以跳过矩阵。
#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( '\n' );
return 0;
}