P8863 「KDOI-03」构造数组
记 表示前 个数凑成 对的方案数。
难点在于如何计算排列的不同方案数。
如果依次加入二元组的第一个和第二个元素便可以用组合数计算方案。
那么转移时枚举和前面匹配的数量即可。
时间复杂度 , 带一个 的常数。
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通