zoukankan      html  css  js  c++  java
  • P2791 幼儿园篮球题

    P2791 幼儿园篮球题

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

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

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

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

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

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


    显然答案是:

    [dfrac{sumlimits_{i=0}^{k}dbinom{m}{i}dbinom{n-m}{k-i}i^L}{dbinom{n}{k}} ]

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

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

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

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

    [sumlimits_{i=0}^{k}dbinom{m}{i}dbinom{n-m}{k-i}sum_{j=0}^{L}egin{Bmatrix}L\jend{Bmatrix}j!dbinom{i}{j}\ =sum_{j=0}^{L}j!egin{Bmatrix}L\jend{Bmatrix} sum_{i=0}^{k}inom{m}{i}inom{i}{j}inom{n-m}{k-i}\ =sum_{j=0}^{L}j!egin{Bmatrix}L\jend{Bmatrix} sum_{i=0}^{k}dfrac{m!}{i!(m-i)!}dfrac{i!}{(i-j)!j!}inom{n-m}{k-i} ]

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

    [=sum_{j=0}^{L}j!egin{Bmatrix}L\jend{Bmatrix}sum_{i=0}^{k}dfrac{m!}{(m-i)!}dfrac{1}{(i-j)!j!}inom{n-m}{k-i}\ =sum_{j=0}^{L}j!egin{Bmatrix}L\jend{Bmatrix}sum_{i=0}^{k}dfrac{(m-j)!}{(m-i)!(i-j)!}dfrac{m!}{j!(m-j)!}inom{n-m}{k-i}\ =sum_{j=0}^{L}j!egin{Bmatrix}L\jend{Bmatrix}sum_{i=0}^{k}inom{m-j}{i-j}inom{m}{j}inom{n-m}{k-i}\ ]

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

    [=sum_{j=0}^{L}j!egin{Bmatrix}L\jend{Bmatrix}inom{m}{j}sum_{i=0}^{k}inom{m-j}{i-j}inom{n-m}{k-i}\ =sum_{j=0}^{L}j!egin{Bmatrix}L\jend{Bmatrix}inom{m}{j}inom{n-j}{k-j} ]

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

    总复杂度 (O(Llog L+SL+N))

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

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


    一些补充:

    [m^n=sum_{i=0}^{m}egin{Bmatrix}n\iend{Bmatrix}i!dbinom{m}{i} ]

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

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

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

    范德蒙德卷积证明:

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

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

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

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

    [m^n=sum_{i=0}^{m}egin{Bmatrix}n\iend{Bmatrix}i!dbinom{m}{i} ]

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

    [egin{Bmatrix}n\mend{Bmatrix}m!=sum_{i=0}^{m}(-1)^{m-i}inom{m}{i}i^n\ egin{Bmatrix}n\mend{Bmatrix}=dfrac{1}{m!}sum_{i=0}^{m}(-1)^{m-i}inom{m}{i}i^n\ egin{Bmatrix}n\mend{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
    ",ans);
    	}
    	return 0;
    }
    

    cxk

  • 相关阅读:
    初步理解Java的三大特性——封装、继承和多态
    设计模式
    区分super和this
    类—矩形面积
    生成不重复的随机数(待完善)
    关于类的创建和调用
    生成验证码
    endsWith和startsWith同样效果其他形式的写法(2016.1.12)
    去除字符串中空格的方法(2016.1.12P141-2)
    java的抽象类
  • 原文地址:https://www.cnblogs.com/zzctommy/p/14251435.html
Copyright © 2011-2022 走看看