【题解】 P4707 重返现世
一道神仙题
首先有 kthmin-max容斥的式子:\(kth\max{S}=\sum_{T\subset S} (-1)^{|T|-k}\tbinom{|T|-1}{k-1}\min{T}\)
证明要用二项式反演,考虑算每个位置作为最大值的贡献。
然后这个东西在期望意义下也是成立的,所以我们就可以把这道题转化成对于每个集合 \(S\) 求出第一次随机到 \(S\) 中元素的期望时间。
这个也很好求,\(val(S)={\frac{1}{\sum_{i \in S}^{} p_i}}\)
但是这道题 \(n\) 高达 \(2000\),我们没法枚举每个集合算答案。
我们注意到 \(m\) 范围是 \(10000\) ,就可以考虑和值域范围有关的 dp ,就是把\(\sum_{i \in S}^{} p_i\) 设到状态里。
设 \(dp_{i,j,k}\) 表示前 \(i\) 个元素形成的集合大小是 \(j\) ,\(p_i\) 的和是 \(k\) 的方案数,转移考虑一个元素是否加入集合。
然后对每个状态算出贡献乘上方案数,70分到手。
考虑优化,我们发现维数太多不好处理,这个时候有个技巧就是把状态藏进权值里。
因为这个 \(\sum_{i \in S}^{} p_i\) 做运算会出现一大堆分数,把他放到权值里会非常的恶心,而权值的另一部分就是一个和 \(|S|\) 有关的式子 \(f(S)\),考虑把他弄进权值里。
设 \(dp_{i,j}\) 表示前 \(i\) 个元素, \(p_i\) 的和是 \(j\) 对应的 \((-1)^{|S|-k}\tbinom{|S|-1}{k-1}\) 的和。
还是考虑这个元素是否加入,不加入的话直接 \(dp_{i,j}+=dp_{i-1,j}\)
加入的话就有意思了,\(dp_{i,j}+=dp_{i-1,j-p_i}\times \text{???}\)
由于我们并不知道集合大小,这个 \(\text{???}\) 也不好算。
把式子稍微写一下 ,原来是 \(\sum_{S} (-1)^{|S|-k}\tbinom{|S|-1}{k-1}\) ,转移之后变成了 \(\sum_{S} (-1)^{|S|+1-k}\tbinom{|S|}{k-1}\)
把这个组合数拆开,变成 \(\sum_{S} (-1)^{|S|+1-k}(\tbinom{|S|-1}{k-2}+\tbinom{|S|-1}{k-1})\)
括号打开,变成 \(\sum_{S} (-1)^{|S|+1-k}\tbinom{|S|-1}{k-2}-\sum_{S} (-1)^{|S|-k}\tbinom{|S|-1}{k-1}\)
我们发现,后边那坨东西不就是 \(dp_{i-1}{j}\) 吗?
前边相当于把 \(k\) 减了个 \(1\),同时我们注意到 \(k\leq 10\) ,于是再记录一维 \(k\) 。
设 \(dp_{i,j,k}\) 表示前 \(i\) 个元素,\(p\) 的和是 \(j\) 的 \((-1)^{|S|-k}\tbinom{|S|-1}{k-1}\) 的和 (注意这里的 \(k\) 不是上面的的 \(k\) 了)
根据上面的智慧东西, \(dp_{i,j,k}=dp_{i-1,j-{p_i},k-1}-dp_{i-1,j-{p_i},k}\)
注意下空间,使用滚动数组压掉第一维,就可以了。
可以了吗???
还有边界问题,这个超级容易漏的。
这里组合数的上边是负的,这时就不能按照组合意义来了,要按照下降幂那个定义来,就是稍微讨论一下就能发现除了 \(k=0\) 的时候是 \(0\) ,剩下的全是 \(-1\) ,直接按照这个设初值。
最后把 \(p_i\) 的贡献也算上就行了。
\(\sf{Code}\)
#include<bits/stdc++.h>
#define N 10010
#define MAX 2001
using namespace std;
typedef long long ll;
typedef double db;
const ll mod=998244353;
inline void read(ll &ret)
{
ret=0;char c=getchar();bool pd=false;
while(!isdigit(c)){pd|=c=='-';c=getchar();}
while(isdigit(c)){ret=(ret<<1)+(ret<<3)+(c&15);c=getchar();}
ret=pd?-ret:ret;
return;
}
ll n,k,m,dp[2][N][15],p[N],ans;
inline ll qpow(ll a,ll b)
{
ll ret=1;
while(b)
{
if(b&1)
ret*=a,ret%=mod;
b>>=1;
a*=a;
a%=mod;
}
return ret;
}
signed main()
{
read(n);
read(k);
read(m);
k=n-k+1;
for(int i=1;i<=n;i++)
read(p[i]);
for(int l=1;l<=k;l++)
dp[0][0][l]=mod-1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
for(int l=0;l<=k;l++)
dp[i&1][j][l]=0;
for(int j=0;j<=m;j++)
{
for(int l=0;l<=k;l++)
{
dp[i&1][j][l]+=dp[(i-1)&1][j][l];
if(j>=p[i]&&l)
dp[i&1][j][l]+=dp[(i-1)&1][j-p[i]][l-1]-dp[(i-1)&1][j-p[i]][l];
if(dp[i&1][j][l]>=mod)
dp[i&1][j][l]-=mod;
if(dp[i&1][j][l]>=mod)
dp[i&1][j][l]-=mod;
if(dp[i&1][j][l]<0)
dp[i&1][j][l]+=mod;
}
}
}
for(int j=1;j<=m;j++)
ans+=dp[n&1][j][k]*qpow(j,mod-2)%mod*m%mod;
printf("%lld\n",ans%mod);
exit(0);
}