P8863 「KDOI-03」构造数组
记 \(f_{i,j}\) 表示前 \(i\) 个数凑成 \(j\) 对的方案数。
难点在于如何计算排列的不同方案数。
如果依次加入二元组的第一个和第二个元素便可以用组合数计算方案。
那么转移时枚举和前面匹配的数量即可。
时间复杂度 \(\mathcal O((\sum b_i)^2)\) , 带一个 \(\frac{1}{4}\) 的常数。
#include <cstdio>
#include <iostream>
using namespace std;
const int MAXN = 5000 , MAXM = 30000 , Mod = 998244353;
inline int Add( int x , int y ) { x += y; return x >= Mod ? x - Mod : x; }
inline int Sub( int x , int y ) { x -= y; return x < 0 ? x + Mod : x; }
inline int Mul( int x , int y ) { return 1ll * x * y % Mod; }
inline int Qkpow( int x , int po ) { int p = 1; for( ; po ; po >>= 1 , x = Mul( x , x ) ) if( po & 1 ) p = Mul( p , x ); return p; }
inline int Inv( int x ) { return Qkpow( x , Mod - 2 ); }
int fac[ MAXM + 5 ] , ivf[ MAXM + 5 ];
void Init( ) {
fac[ 0 ] = 1;
for( int i = 1 ; i <= MAXM ; i ++ ) fac[ i ] = Mul( fac[ i - 1 ] , i );
ivf[ MAXM ] = Inv( fac[ MAXM ] );
for( int i = MAXM ; i >= 1 ; i -- ) ivf[ i - 1 ] = Mul( ivf[ i ] , i );
}
inline int C( int n , int m ) { return n < m ? 0 : Mul( fac[ n ] , Mul( ivf[ m ] , ivf[ n - m ] ) ); }
int n , m , b[ MAXN + 5 ] , s[ MAXN + 5 ];
int f[ 2 ][ MAXM + 5 ];
int main( ) {
Init();
scanf("%d",&n);
for( int i = 1 ; i <= n ; i ++ ) {
scanf("%d",&b[ i ]); m += b[ i ];
s[ i ] = s[ i - 1 ] + b[ i ];
}
if( m & 1 ) return puts("0") & 0; m /= 2;
f[ 0 ][ 0 ] = 1;
for( int i = 1 ; i <= n ; i ++ )
for( int j = 0 ; j <= m ; j ++ ) {
f[ i & 1 ][ j ] = 0;
for( int k = 0 ; k <= min( j , b[ i ] ) ; k ++ )
f[ i & 1 ][ j ] = Add( f[ i & 1 ][ j ] , Mul( f[ ( i - 1 ) & 1 ][ j - k ] , Mul( C( s[ i - 1 ] - 2 * ( j - k ) , k ) , C( m - ( j - k ) - ( s[ i - 1 ] - 2 * ( j - k ) ) , b[ i ] - k ) ) ) );
}
printf("%d\n", f[ n & 1 ][ m ] );
return 0;
}