Loading

P2791 幼儿园篮球题

P2791 幼儿园篮球题

做这道题想说的事有点多呢,前言有点长,可以跳过。

萌新刚刚看了一点斯特林数相关的内容,这是我见到的第二道题。第一道题看了一个上午没看懂题解就放弃了。由于有了第一题铺垫,这题实在是太naive了。

先来说说这题几个值的关注的地方(也是我拿到题的第一反应):

  • 最显眼的:有个cxk动图。
  • 其次,时限不是普通的1s或1.5s或2s什么的,而是1.11s
  • 顺便发现,空间也是三个一样的数组成的:222MB
  • 输入是NMSL,疑似骂人

你可能觉得我啥也没说,但是我确实栽在了上面的一个地方/fad

写代码的时候,要卷第二类斯特林数。忽然想起这题曾经在丁爸那里当作作业题出过,一年前的事了,也就是我刚知道FTT那会。当时不会,去问的whk作为一个OIer这名字挺不吉利的awa,还是他qq上教我的qwq。现在他退役了,去搞别的竞赛了。他大概是我们一批人中间最早离开的了/kk。想起现在看不到他了就莫名伤心。上次CSP前问过他,状态不好怎么办。他说他就是这么退役的。现在我的状态过了瓶颈期,多项式救了我。而他没能扛过去。我想他再等几个月说不定就能顺利通过瓶颈,水平飞越,AKIOI。唉,就这样,一个OIer退役了。祝好,希望我们都有光明的前途。


显然答案是:

\[\dfrac{\sum\limits_{i=0}^{k}\dbinom{m}{i}\dbinom{n-m}{k-i}i^L}{\dbinom{n}{k}} \]

就是枚举cxk投进了几个球而已。

只看分子。那两个组合数一脸范德蒙德卷积,但是后面带了一个系数没用。

后面那个幂次考虑把它写成第二类斯特林数的形式。

你可能问我为啥想到斯特林数。见前言,我见到过一个比这题恶心的多的题,这个是基础套路,相比之下这题所有的操作都太基础了。

\[\sum\limits_{i=0}^{k}\dbinom{m}{i}\dbinom{n-m}{k-i}\sum_{j=0}^{L}\begin{Bmatrix}L\\j\end{Bmatrix}j!\dbinom{i}{j}\\ =\sum_{j=0}^{L}j!\begin{Bmatrix}L\\j\end{Bmatrix} \sum_{i=0}^{k}\binom{m}{i}\binom{i}{j}\binom{n-m}{k-i}\\ =\sum_{j=0}^{L}j!\begin{Bmatrix}L\\j\end{Bmatrix} \sum_{i=0}^{k}\dfrac{m!}{i!(m-i)!}\dfrac{i!}{(i-j)!j!}\binom{n-m}{k-i} \]

做上面这步阶乘分拆是因为看到了 \(i!\) 可以约掉并且配成新的组合数。

\[=\sum_{j=0}^{L}j!\begin{Bmatrix}L\\j\end{Bmatrix}\sum_{i=0}^{k}\dfrac{m!}{(m-i)!}\dfrac{1}{(i-j)!j!}\binom{n-m}{k-i}\\ =\sum_{j=0}^{L}j!\begin{Bmatrix}L\\j\end{Bmatrix}\sum_{i=0}^{k}\dfrac{(m-j)!}{(m-i)!(i-j)!}\dfrac{m!}{j!(m-j)!}\binom{n-m}{k-i}\\ =\sum_{j=0}^{L}j!\begin{Bmatrix}L\\j\end{Bmatrix}\sum_{i=0}^{k}\binom{m-j}{i-j}\binom{m}{j}\binom{n-m}{k-i}\\ \]

发现把 \(\dbinom{m}{j}\) 提到前面去就可以范德蒙德卷积了。

\[=\sum_{j=0}^{L}j!\begin{Bmatrix}L\\j\end{Bmatrix}\binom{m}{j}\sum_{i=0}^{k}\binom{m-j}{i-j}\binom{n-m}{k-i}\\ =\sum_{j=0}^{L}j!\begin{Bmatrix}L\\j\end{Bmatrix}\binom{m}{j}\binom{n-j}{k-j} \]

用一点多项式技巧求预处理出第 \(L\) 行第二类斯特林数,再预处理一下阶乘,对于每一个询问即可 \(O(L)\) (当 \(n,m\) 较小时可以更快)。

总复杂度 \(O(L\log L+SL+N)\)

哦对了,说一下我栽在哪里了。空间是222MB,由于习惯预处理了阶乘,阶乘逆元,还有 \([1,n]\) 的逆元,于是空间228MB,MLE了。最后一个不用处理的。

upd:我发现一个都不用预处理,所以空间可以 \(O(L)\)


一些补充:

\[m^n=\sum_{i=0}^{m}\begin{Bmatrix}n\\i\end{Bmatrix}i!\dbinom{m}{i} \]

众所周知第二类斯特林数 \(\begin{Bmatrix}n\\m\end{Bmatrix}\) 的组合意义是,\(n\) 个不同的球放进 \(m\) 个相同的盒子且盒子不能为空的方案数

\(m^n\) 的组合意义是 \(n\) 个相同球放进 \(m\) 个不同盒子的方案数

那么等式右边就是,枚举 \(i\) 个非空的盒子,然后把 \(n\) 个球放进去。同时,由于无标号变成了有标号要乘上阶乘。

范德蒙德卷积证明:

\[(1+x)^{n+m}=(1+x)^{n}(1+x)^{m}\\ \]

提取 \([x^k]\) 系数:

\[\binom{n+m}{k}=\sum_{i=0}^{k}\binom{n}{i}\binom{m}{k-i} \]

如何求某一行的斯特林数/斯特林数与组合数的关系。

\[m^n=\sum_{i=0}^{m}\begin{Bmatrix}n\\i\end{Bmatrix}i!\dbinom{m}{i} \]

二项式反演得(\(m^n\) 看作一个函数,\(\begin{Bmatrix}n\\i\end{Bmatrix}i!\) 看作一个函数)

\[\begin{Bmatrix}n\\m\end{Bmatrix}m!=\sum_{i=0}^{m}(-1)^{m-i}\binom{m}{i}i^n\\ \begin{Bmatrix}n\\m\end{Bmatrix}=\dfrac{1}{m!}\sum_{i=0}^{m}(-1)^{m-i}\binom{m}{i}i^n\\ \begin{Bmatrix}n\\m\end{Bmatrix}=\sum_{i=0}^{m}\dfrac{(-1)^{m-i}}{(m-i)!}\dfrac{i^n}{i!} \]

第三行可以卷积用,第二行在某些毒瘤题要你暴力展开第二类斯特林数要用。

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mkp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define sz(v) (int)v.size()
typedef long long LL;
typedef double db;
template<class T>bool ckmax(T&x,T y){return x<y?x=y,1:0;}
template<class T>bool ckmin(T&x,T y){return x>y?x=y,1:0;}
#define rep(i,x,y) for(int i=x,i##end=y;i<=i##end;++i)
#define per(i,x,y) for(int i=x,i##end=y;i>=i##end;--i)
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return f?x:-x;
}
const int N=200005;
const int M=N<<2;
const int K=20000005;
#define mod 998244353
int n,m,s,l,sti[N],k,ans;
namespace math{
int fac[K],ifc[K];
inline int qpow(int n,int k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*n*res%mod;return res;}
inline void fmod(int&x){x-=mod,x+=x>>31&mod;}
inline int comb(int n,int m){return n<m?0:1ll*fac[n]*ifc[m]%mod*ifc[n-m]%mod;}
void initmath(const int&n=K-1){
	fac[0]=1;for(int i=1;i<=n;++i)fac[i]=1ll*i*fac[i-1]%mod;
	ifc[n]=qpow(fac[n],mod-2);for(int i=n-1;i>=0;--i)ifc[i]=1ll*ifc[i+1]*(i+1)%mod;
}
}
using math::qpow;
using math::fmod;
namespace poly{
int rev[M],lg,lim;
void init_poly(const int&n){
	for(lg=0,lim=1;lim<n;lim<<=1,++lg);
	for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
}
void NTT(int*a,int op){
	for(int i=0;i<lim;++i)
		if(i>rev[i])swap(a[i],a[rev[i]]);
	const int g=op?3:qpow(3,mod-2);
	for(int i=1;i<lim;i<<=1){
		const int wn=qpow(g,(mod-1)/(i<<1));
		for(int j=0;j<lim;j+=i<<1){
			int w0=1;
			for(int k=0;k<i;++k,w0=1ll*w0*wn%mod){
				const int X=a[j|k],Y=1ll*w0*a[i|j|k]%mod;
				fmod(a[j|k]=X+Y),fmod(a[i|j|k]=X-Y+mod);
			}
		}
	}
	if(op)return;const int ilim=qpow(lim,mod-2);
	for(int i=0;i<lim;++i)a[i]=1ll*a[i]*ilim%mod;
}
#define clr(a,n) memset(a,0,sizeof(int)*(n))
#define cpy(a,b,n) memcpy(a,b,sizeof(int)*(n))
void poly_mul(int*f,int*g,int*ans,int n,int m){
	static int A[M],B[M];init_poly(n+m);
	cpy(A,f,n),clr(A+n,lim-n),NTT(A,1);
	cpy(B,g,m),clr(B+m,lim-m),NTT(B,1);
	for(int i=0;i<lim;++i)ans[i]=1ll*A[i]*B[i]%mod;
	NTT(ans,0);
}
void get_stirling(int n,int*sti){//第n行
	static int A[M],B[M];
	for(int i=0;i<=n;++i)A[i]=i&1?mod-math::ifc[i]:math::ifc[i],B[i]=1ll*qpow(i,n)*math::ifc[i]%mod;
	poly_mul(A,B,A,n+1,n+1);
	for(int i=0;i<=n;++i)sti[i]=A[i];
}

}
signed main(){
	n=read(),m=read(),s=read(),l=read();
	math::initmath(n);
	poly::get_stirling(l,sti);
	while(s--){
		n=read(),m=read(),k=read(),ans=0;
		for(int up=min(n,min(m,l)),i=0;i<=up;++i){
			fmod(ans+=1ll*math::fac[i]*sti[i]%mod*math::comb(m,i)%mod*math::comb(n-i,k-i)%mod);
		}
		ans=1ll*ans*qpow(math::comb(n,k),mod-2)%mod;
		printf("%d\n",ans);
	}
	return 0;
}

cxk

posted @ 2021-01-08 14:58  zzctommy  阅读(284)  评论(2编辑  收藏  举报