21.6.29 t3
tag:minmax反演,指数型生成函数,概率期望
屑模拟赛放原题
如果写出操作序列,设 \(a_{i,j}\) 表示 \(i\) 第 \(j\) 次出现是在 \(a_{i,j}\) 位置。
那么题目要求的就是 \(\max a_{i,k}\)。
运用minmax反演
\[ans=\sum_{S\subseteq\{1\cdots n\},S\not=\varnothing}(-1)^{|S|+1}\min(S)
\]
也就是枚举一个集合,求这个集合中任意一个物品出现 \(k\) 次的期望步数。
这样就可以枚举所有可能的序列长度了(原问题可以是无穷序列)
对于长度为 \(len\) 的操作序列,先选出 \(\max\) 元素和它的位置 \(|S|\cdot\binom{len-1}{k-1}\)(因为最后一个一定是 \(\max\) 元素)。
然后问题变为,\(|S|\) 个物品放到长度为 \(len-k\) 的序列中,每个物品不能出现超过 \(k-1\) 次。
这个也就是
\[[x^{len-k}](\sum_{i=0}^{k-1}\frac{x^i}{i!})^{|S|-1}
\]
可以在最开始预处理出来,用fft复杂度为 \(O(nklog(nk))\)。
然后还要注意考虑集合外元素的贡献(根据 \(a_{i,j}\) 的定义,不能只考虑 \(S\) 中的元素)
问题变为 \(a\) 个黑球,\(b\) 个 白球,每次随机拿出一个并放回,求期望多少次拿出第一个白球。
这个是概率期望经典问题,期望就是概率的倒数 \(\frac {a+b}b\)。(实在想不明白可以暴力等比数列求和)
相当于期望步长为 \(\frac n{|S|}\),直接乘上就行。
然后还要乘上每个序列的出现概率,\((\frac 1{|S|})^{len}\)
然后注意到 \(S\) 的贡献只与大小有关,所以改为枚举大小就行了。
\[ans=\sum_{|S|=1}^n\binom n{|S|}\sum_{len=k}^{|S|(k-1)+1}([x^{len-k}](\sum_{i=0}^{k-1}\frac{x^i}{i!})^{|S|-1}\cdot |S|\cdot\binom{len-1}{k-1}\cdot (\frac 1{|S|})^{len}\cdot len\cdot \frac n{|S|})
\]
复杂度 \(O(nklog(nk)-n^2k)\),不知道为什么数据范围这么小。。
#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void Read(T &n){
char ch; bool flag=false;
while(!isdigit(ch=getchar()))if(ch=='-')flag=true;
for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
if(flag)n=-n;
}
enum{
MAXN = 50005,
MOD = 998244353
};
inline int ksm(int base, int k=MOD-2){
int res=1;
while(k){
if(k&1)
res = 1ll*res*base%MOD;
base = 1ll*base*base%MOD;
k >>= 1;
}
return res;
}
inline long long kspow(long long base, long long k){
long long res=1;
while(k){
if(k&1)
res *= base;
base *= base;
k >>= 1;
}
return res;
}
inline int inc(int a, int b){
a += b;
if(a>=MOD) a -= MOD;
return a;
}
inline int dec(int a, int b){
a -= b;
if(a<0) a += MOD;
return a;
}
inline void iinc(int &a, int b){a = inc(a,b);}
inline void ddec(int &a, int b){a = dec(a,b);}
inline void upd(int &a, long long b){a = (a+b)%MOD;}
namespace FFT{
int f[MAXN<<2], g[MAXN<<2], wn[MAXN<<2], tr[MAXN<<2];
inline int prework(int n){
int len=1; while(len<=n) len<<=1;
for(int i=0; i<len; i++) tr[i] = (tr[i>>1]>>1)|((i&1)?len>>1:0);
wn[0] = 1; wn[1] = ksm(3,(MOD-1)/len);
for(int i=2; i<=len; i++) wn[i] = 1ll*wn[i-1]*wn[1]%MOD;
return len;
}
inline void fft(int *f, int n, int flag){
for(int i=0; i<n; i++) if(i<tr[i]) swap(f[i],f[tr[i]]);
for(int len=2; len<=n; len<<=1){
int base = n/len*flag;
for(int l=0; l<n; l+=len){
int now = (flag==1?0:n);
for(int i=l; i<l+len/2; i++){
int tmp = 1ll*f[i+len/2]*wn[now]%MOD;
f[i+len/2] = dec(f[i],tmp);
iinc(f[i],tmp);
now += base;
}
}
}
if(flag==-1){
int invn = ksm(n);
for(int i=0; i<n; i++) f[i] = 1ll*f[i]*invn%MOD;
}
}
}
using FFT::fft;
using FFT::prework;
int n, k, tp, jc[MAXN], invjc[MAXN], inv[51], f[51][MAXN];
int pw[51][MAXN];
inline void mul(int pos){
for(int i=0; i<=(pos-1)*k; i++) FFT::f[i] = f[pos-1][i];
fft(FFT::f,tp,1);
for(int i=0; i<tp; i++) f[pos][i] = 1ll*FFT::f[i]*FFT::g[i]%MOD;
fft(f[pos],tp,-1);
fill(FFT::f,FFT::f+tp,0);
}
inline int C(int n, int m){return 1ll*jc[n]*invjc[m]%MOD*invjc[n-m]%MOD;}
int main(){
Read(n); Read(k);
f[0][0] = 1; invjc[0] = jc[0] = 1; tp = prework((n-1)*(k-1));
for(int i=1; i<=n*k; i++) jc[i] = 1ll*jc[i-1]*i%MOD, invjc[i] = 1ll*invjc[i-1]*ksm(i)%MOD;
for(int i=0; i<k; i++) FFT::g[i] = invjc[i]; fft(FFT::g,tp,1);
for(int i=1; i<=n; i++){
pw[i][0] = 1; pw[i][1] = inv[i] = ksm(i);
for(int j=2; j<=n*k; j++) pw[i][j] = 1ll*pw[i][j-1]*pw[i][1]%MOD;
}
for(int i=1; i<n; i++) mul(i);
for(int i=0; i<n; i++) for(int j=0; j<=i*(k-1); j++) f[i][j] = 1ll*f[i][j]*jc[j]%MOD;
int ans=0;
for(int i=1; i<=n; i++){
int dlt=0;
for(int j=k-1; j<=i*(k-1); j++)
upd(dlt,1ll*i*C(j,k-1)%MOD*f[i-1][j-k+1]%MOD*(j+1)%MOD*pw[i][j+1]%MOD*n%MOD*inv[i]);
if(i&1) upd(ans,1ll*dlt*C(n,i));
else ddec(ans,1ll*dlt*C(n,i)%MOD);
}
cout<<ans<<'\n';
return 0;
}
/*
3 5
537614966
*/