Soulist
2019-12-07 16:26:40
首先可以发现题目要求的为:
套用拓展
得到:
我们知道原来要求的是全集合中出现时间排第
我们知道一个集合每次操作出现一个属于它的元素的概率为:
于是我们知道期望时间为:
于是现在我们得到了一个复杂度为
然后我就不会了....
接下来看来需要一点魔法了...
我们来看下这个式子:
考虑一个固定的集合大小
则我们只需要求出
注意到
于是可以得到,不妨记
于是这样便可以得到一个复杂度为
于是问题在于如何优化复杂度?
我们发现转移已经是最优了,不能从这里下手于是只能从状态下手
由于
换而言之我们只统计,到第
考虑系数为:
拆开试试?
好像还是不行...
但是这个时候可以得到转移大致为:
这个奇怪的东西应该表示为:
对于
首先可以注意到所有
注意到
但是我们知道组合数可以递推所以有:
然而真正有趣的是这个式子可以放在一起一起递推,因为考虑计算
即:
这个
接下来考虑边界条件,这个好像有点难,因为我们的
考虑边界问题,我们知道
于是考虑前面的数,唯一的一个
这个时候我们需要拓宽组合数的意义,你可以认为
当然转移的过程可以滚动数组整体复杂度
#include<bits/stdc++.h>
using namespace std ;
#define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
#define re register
#define int long long
int gi() {
char cc = getchar() ; int cn = 0, flus = 1 ;
while( cc < '0' || cc > '9' ) { if( cc == '-' ) flus = - flus ; cc = getchar() ; }
while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ;
return cn * flus ;
}
const int N = 1000 + 5 ;
const int M = 10000 + 5 ;
const int P = 998244353 ;
int n, m, t, Ans, inv[M], p[N], dp[14][M] ;
int fpow( int x, int k ) {
int ans = 1, base = x ;
while( k ) {
if( k & 1 ) ans = ans * base % P ;
base = base * base % P, k >>= 1 ;
} return ans ;
}
signed main()
{
n = gi(), t = n - gi() + 1, m = gi() ;
rep( i, 1, n ) p[i] = gi() ; inv[0] = dp[0][0] = 1 ;
rep( i, 1, m ) inv[i] = fpow( i, P - 2 ) % P ;
for( re int i = 1; i <= n; ++ i ) {
for( re int k = m; k >= p[i]; -- k )
for( re int j = t; j; -- j )
dp[j][k] = ( dp[j - 1][k - p[i]] - dp[j][k - p[i]] + dp[j][k] + P ) % P ;
}
for( re int k = 0; k <= m; ++ k ) Ans = ( Ans + dp[t][k] * inv[k] % P ) % P ;
printf("%lld\n", Ans * m % P ) ;
return 0 ;
}