crashed
2022-02-14 21:50:58
雨打在窗沿,下坠,一级一级。
这里一共有
设在「下一个瞬间」,第
两个「下一个瞬间」本质不同,当且仅当存在编号
对于
想必就不用我多说了吧
可以直接搜索每个窗沿到其它窗沿的雨滴数量。
复杂度看实现,不过肯定是能过的。
这个部分分还是蛮重要的。
我们考虑将题目提到的方法扩展一下。设
考虑直接拆开和式。我们形式地写为:
我们可以尝试将同一下标的
不难发现此时某一个
时间复杂度为
类似于 Subtask 5 的处理,设
同样的处理方法,我们把外层
不妨考虑这样一个
那么,考虑外层
接下来,尝试收拢内层的和式套乘积式。不妨设
有
r_i 个盒子,编号为0,1,2,\dots,r_i-1 。其中编号落在S_i 中的盒子,放了x 个球会贡献x ;而其它盒子无论放多少个球,贡献都是1 。一种方案的贡献为各个盒子的贡献的积。求往r_i 个盒子中任意放a_i 个没有区别的球的贡献之和。
事实上,我们发现答案和
再来看如何将巨大的乘积式拆分成若干个单项式。由于答案计算的过程中,只有
所以,现在可以解决 Subtask 6。此时每个
时间复杂度可以做到
进一步拓展 Subtask 6 中的处理方法,我们压一个状态
如果实现不恰当,复杂度会到
时间复杂度可以做到
这个部分分还可以进行容斥。我们要求所有的位置最终必须被占用,对被占用时是否在有效的
此时直接莽
实现时,可以设状态
两侧平衡一下复杂度,理论上总的时间复杂度最小可以为 但是,考虑到 。
其实想到了容斥方法基本就解决本题了。
同样考虑的是,
结合 Subtask 7 的算法和 Subtask 9 的算法即可得到
#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 MAXN = 100;
template<typename _T>
void read( _T &x ) {
x = 0; char s = getchar(); bool f = false;
while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
if( f ) x = -x;
}
template<typename _T>
void write( _T x ) {
if( x < 0 ) putchar( '-' ), x = -x;
if( 9 < x ) write( x / 10 );
putchar( x % 10 + '0' );
}
template<typename _T>
_T MIN( const _T a, const _T b ) {
return a < b ? a : b;
}
int C[MAXN][MAXN];
int A[MAXN], coe[MAXN][MAXN];
int inv[MAXN];
int N, K;
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 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;
}
void Init( const int k ) {
inv[1] = 1; rep( i, 2, k << 1 ) inv[i] = Mul( inv[mod % i], mod - mod / i );
rep( i, 0, k ) {
C[i][0] = C[i][i] = 1;
rep( j, 1, i - 1 ) C[i][j] = Add( C[i - 1][j], C[i - 1][j - 1] );
}
}
int Binom( const int x, const int r ) {
if( x < r ) { return 0; } int ret = 1;
rep( i, 1, r ) ret = Mul( ret, Mul( ( x - i + 1 ) % mod, inv[i] ) );
return ret;
}
int Query( const int j, const int k, const int n ) {
if( n < k ) return 0;
return Binom( n + j - 1, j + k - 1 );
}
namespace SmallK {
const int MAXS = ( 1 << 16 ) + 5;
int f[MAXS], g[MAXS][MAXN];
void Solve() {
if( K == 0 ) {
int ans = 1;
rep( i, 1, N ) ans = Mul( ans, A[i] );
write( ans ), putchar( '\n' );
return ;
}
f[0] = coe[N][0], f[1] = coe[N][1];
per( i, N - 1, 1 ) {
int lim = MIN( N - i + 1, K + 1 );
rep( j, 0, ( 1 << lim ) - 1 ) rep( k, 0, lim ) g[j][k] = 0;
rep( j, 0, ( 1 << ( lim - 1 ) ) - 1 ) g[j][0] = f[j], f[j] = 0;
rep( j, 0, lim - 1 )
per( k, ( 1 << lim ) - 1, 0 )
if( ! ( k >> j & 1 ) ) per( t, lim - 1, 0 )
g[k | ( 1 << j )][t + 1] = Add( g[k | ( 1 << j )][t + 1], g[k][t] );
if( i <= N - K ) {
rep( k, 0, ( 1 << lim ) - 1 )
if( k & 1 ) rep( t, 0, lim )
f[k >> 1] = Add( f[k >> 1], Mul( g[k][t], coe[i][t] ) );
} else {
rep( k, 0, ( 1 << lim ) - 1 )
rep( t, 0, lim ) f[k] = Add( f[k], Mul( g[k][t], coe[i][t] ) );
}
}
write( f[( 1 << K ) - 1] ), putchar( '\n' );
}
}
namespace LargeK {
int f[MAXN];
void Solve() {
int rest = N - K - 1, ans = 0, lim, tmp, shft = 0;
for( unsigned S = 0 ; S < ( 1u << rest ) ; S ++ ) {
f[0] = 1, lim = 0;
per( i, N, 1 ) {
shft = 0;
if( i > K + 1 ) shft += ! ( S >> ( i - K - 2 ) & 1 );
else shft ++;
if( i + K < N ) shft += S >> ( i - 1 ) & 1;
if( shft ) {
per( j, lim, 0 ) f[j + shft] = f[j];
rep( j, 0, shft - 1 ) f[j] = 0;
lim += shft;
}
rep( j, 0, lim ) {
tmp = 0;
for( int t = 0 ; t <= K + 1 && t + j <= lim ; t ++ )
tmp = Add( tmp, Mul( f[j + t], Mul( C[j + t][t], coe[i][t] ) ) );
f[j] = tmp;
}
}
ans = __builtin_popcount( S ) & 1 ? Sub( ans, f[0] ) : Add( ans, f[0] );
}
write( ans ), putchar( '\n' );
}
}
int main() {
read( N ), read( K );
Init( K + 1 );
rep( i, 1, N ) {
read( A[i] );
int lim = MIN( N - i + 1, K + 1 );
rep( j, 0, lim ) coe[i][j] = Query( lim, j, A[i] );
}
if( K < N / 2 )
SmallK :: Solve();
else
LargeK :: Solve();
return 0;
}