UOJ449 【集训队作业2018】喂鸽子

Link
先套一个最值反演,显然大小相同的集合的答案是一样的,所以要求的是(f_i)表示(i)只鸽子第一次有某只鸽子被喂满的期望次数。最后答案为(sumlimits_{i=1}^n(-1)^{i+1}{nchoose i}frac nif_i)
现在钦定(i)只鸽子,我们称丢给这几只鸽子的玉米为有效玉米,那么取到(z)个有效玉米的期望步数就是(frac{zn}i)
那么我们可以枚举有效玉米序列的长度(j),计算出有贡献的方案数,然后除以总方案数(i^j)
计算有贡献的可以考虑EGF,钦定第一个吃饱的鸽子是(1),把最后的方案数乘上(i)即可。
那么此时最后一个有效玉米是确定了的,所以只需要知道第一只吃了(k-1)个,其它的不超过(k-1)个的方案数,可以轻松得到EGF为

[frac{x^{k-1}}{(k-1)!}(sumlimits_{j=0}^{k-1}frac{x^j}{j!})^{i-1} ]

(frac{x^{k-1}}{(k-1)!})是容易处理的,那么我们现在只需要处理((sumlimits_{j=0}^kfrac{x^j}{j!})^i)
考虑利用万能的求导,记(f(x)=exp xmod x^{k+1})

[egin{aligned} frac{mathrm df^i}{mathrm dx}=if^{i-1}f'=if^{i-1}(f-frac{x^k}{k!})=if^i-frac{x^k}{k!}f^{i-1} end{aligned}]

#include<cstdio>
#include<cstring>
using i64=long long;
const int N=50007,P=998244353;
i64 fac[N],ifac[N],inv[N],f[N];
void inc(i64&a,i64 b){a+=b-P,a+=a>>63&P;}
void dec(i64&a,i64 b){a-=b,a+=a>>63&P;}
i64 binom(int n,int m){return fac[n]*ifac[m]%P*ifac[n-m]%P;}
i64 pow(i64 a,int b){i64 r=1;for(;b;b>>=1,a=a*a%P)if(b&1)r=r*a%P;return r;}
int main()
{
    int n,k;i64 ans=0;scanf("%d%d",&n,&k),f[0]=fac[0]=fac[1]=inv[0]=inv[1]=ifac[0]=ifac[1]=1;
    for(int i=2;i<=n*k;++i) fac[i]=i*fac[i-1]%P,inv[i]=(P-P/i)*inv[P%i]%P,ifac[i]=inv[i]*ifac[i-1]%P;
    for(int i=1;i<=n;++i)
    {
	static i64 t[N];t[0]=1;i64 sum=0,pw=1;
	if(k!=1) for(int j=i*k;j>=k-1;--j) f[j]=f[j-k+1]*ifac[k-1]%P,f[j-k+1]=0;
	for(int j=1;j<=i*k;++j) t[j]=i*inv[j]%P*(P+t[j-1]-f[j-1])%P;
	memcpy(f,t,sizeof t);
	for(int j=0;j<=i*k;++j) inc(sum,pw*fac[j]%P*f[j]%P),pw=pw*inv[i]%P;
	(i&1? inc:dec)(ans,binom(n,i)*sum%P*n%P*inv[i]%P);
    }
    printf("%lld",ans);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/13046423.html