• 【学术篇】CF932E Team Work && bzoj5093 图的价值


    两个题传送门

    对于CF这道题, 分别考虑每种可能的集合大小, 每个大小为(k)的集合数量有(inom nk)个, 所以最后的答案就是

    [sum_{i=0}^ninom{n}{i}i^k ]

    对于bzoj这道题, 我们分别考虑每个点的贡献, 这个点可以和其他(n-1)个点连任意条边, 贡献是(sum_{i=0}^{n-1}inom{n-1}ii^k)
    此时其他(n-1)个点间的(frac{(n-1)(n-2)}2)条边可连可不连, 所以有(2^{frac{(n-1)(n-2)}2})种选择, 最后再乘以点数, 我们要求的就是

    [n*2^{frac{(n-1)(n-2)}2}*sum_{i=0}^{n-1}inom{n-1}ii^k ]

    我们看这个(n-1)非常不爽, 不妨将读进来的(n)直接自减一下代替(n-1), 那我们要求的就是

    [(n+1)*2^{frac{n(n-1)}2}*sum_{i=0}^ninom nii^k ]

    前面的乘法和快速幂都贼jr简单, 所以我们问题的关键也就变得和上面一样, 求

    [sum_{i=0}^ninom nii^k ]


    然后一看这个式子直接算肯定是完全过不去的, 我们就要化式子.
    我们首先就要根据第二类斯特林数的性质把(k)次幂转化为下降幂.
    (你问什么是下降幂? 就是说(n^underline{k}=n(n-1)(n-2)cdots(n-k+1))这样的啊~如果你愿意也可以看成(n!/(n-k)!))

    [=sum_{i=0}^ninom{n}{i}sum_{j=0}^kegin{Bmatrix}k\jend{Bmatrix}i^{underline{j}} ]

    然后把组合数和下降幂都展开发现可以前后约掉一个(i!)

    [=sum_{i=0}^nfrac{n!}{(n-i)!}sum_{j=0}^kegin{Bmatrix}k\jend{Bmatrix}frac 1{(i-j)!} ]

    然后收拾下式子, 把(n!)提出来, (sum)提前,

    [=n!sum_{i=0}^nsum_{j=0}^kegin{Bmatrix}k\jend{Bmatrix}frac1{(n-i)!}cdotfrac{1}{(i-j)!} ]

    然后我们发现后面可以搞成一个组合数的形式, 我们考虑乘一个((n-j)!)化成组合数, 然后再把它除掉...

    [=n!sum_{i=0}^nsum_{j=0}^kegin{Bmatrix}k\jend{Bmatrix}inom {n-j}{n-i}cdotfrac 1{(n-j)!} ]

    我们把只和(j)有关的式子提到前面, 顺便把(n!)重新乘进去

    [=sum_{j=0}^kegin{Bmatrix}k\jend{Bmatrix}frac{n!}{(n-j)!}sum_{i=0}^ninom{n-j}{n-i} ]

    由于(i)(0)取到(n), 所以组合数一定能取遍([0,n-j])的每个数, 而且比(n-j)大的部分组合数都是0. 所以由组合数的性质可知$$sum_{i=0}ninom{n-j}{n-i}=2{n-j}$$
    所以最终可以把原式化为

    [=sum_{j=0}^kegin{Bmatrix}k\jend{Bmatrix}n^{underline{j}}2^{n-j} ]

    众所周知, 第二类斯特林数说的是

    (n)个球放到(k)个盒子中, 要求盒子非空的方案数. (盒子和球均无标号)

    所以如果前(n-1)个球放到了(k-1)个盒子中, 那么第(n)个球只好放到第(k)个集合中; 而如果前(n-1)个球放进了(k)个盒子, 那么第(n)个球只要放到任意一个盒子中就行了.

    [egin{Bmatrix}n\kend{Bmatrix}=egin{Bmatrix}n-1\k-1end{Bmatrix}+egin{Bmatrix}n-1\kend{Bmatrix}*k ]

    很显然这样处理斯特林数是(O(k^2))的, 对于CF上这道题来说, (kleq5000), 这样做就可以跑过了. AC代码:

    #include <cstdio>
    const int p=1e9+7;
    int s[5050][5050],cur[5050];
    int qpow(int a,long long b,int s=1){
    	for(;b;b>>=1,a=1ll*a*a%p)
    		if(b&1) s=1ll*s*a%p;
    	return s;
    }
    int main(){
    	int n,k,ans=0; scanf("%d%d",&n,&k);
    	s[0][0]=cur[0]=1;
    	for(int i=1;i<=k;++i){
    		cur[i]=1ll*cur[i-1]*(n-i+1)%p;
    		for(int j=1;j<=k;++j){
    			s[i][j]=s[i-1][j-1]+1ll*s[i-1][j]*j%p;
    			if(s[i][j]>=p) s[i][j]-=p;
    		}
    	}
    	for(int i=0;i<=k&&i<=n;++i){
    		ans+=1ll*qpow(2,n-i,s[k][i])*cur[i]%p;
    		if(ans>=p) ans-=p;
    	} printf("%d",ans);
    }
    

    但是对于bzoj上这道题来说, (kleq 200000), 再(O(k^2))预处理就不行了. 我们考虑还有没有别的方法来处理斯特林数.
    我们注意到实际上我们用到的斯特林数只有第(k)行这一行的数, 我们没有必要求出前(k-1)行来.
    我们假设现在有(n)个球要放到(m)个盒子中, 我们可以让先给盒子标号, 最后的方案数除以(m!)即可.
    然后我们枚举空盒子的数量(k), 一共有(inom mk)种空盒子的选择方案, 然后(n)个球可以在((m-k))个盒子中随便放.
    然后根据容斥我们就可以得出

    [egin{Bmatrix}n\mend{Bmatrix}=frac 1{m!}sum_{k=0}^m(-1)^kinom mk(m-k)^n ]

    我们把组合数拆开

    [egin{Bmatrix}n\mend{Bmatrix}=sum_{k=0}^mfrac {(-1)^k}{k!}cdotfrac{(m-k)^n}{(m-k)!} ]

    然后我们令(f(x)=frac{(-1)^x}{x!}, g(x)=frac{x^n}{x!}), 就可以发现

    [egin{Bmatrix}n\mend{Bmatrix}=sum_{k=0}^mf(k)*g(m-k) ]

    是个明显的卷积形式, 而题目中又给了(998244353)作为模数, 自然NTT一下就可以咯~(其实这是我写的第一道卷积非模板题)代码:

    #include <cstdio>
    #include <algorithm>
    const int N=606060,p=998244353;
    int wn[20],nw[20],f[N],g[N],rev[N],n,lg;
    int m,k,fac[N],cur[N],inv[N];
    int qpow(int a,long long b,int s=1){
    	for(;b;b>>=1,a=1ll*a*a%p)
    		if(b&1ll) s=1ll*s*a%p;
    	return s;
    }
    void calcw(){
    	int x=qpow(3,p-2);
    	for(int i=0;i<20;++i){
    		wn[i]=qpow(3,(p-1)/(1<<i));
    		nw[i]=qpow(x,(p-1)/(1<<i));
    	}
    }
    void init(){		
    	for(int i=0;i<n;++i)
    		rev[i]=(rev[i>>1]>>1)|((i&1)<<lg);
    	fac[0]=inv[0]=cur[0]=1;
    	for(int i=1;i<=k;++i) fac[i]=1ll*fac[i-1]*i%p;
    	for(int i=1;i<=k;++i) cur[i]=1ll*cur[i-1]*(m-i+1)%p;
    	inv[k]=qpow(fac[k],p-2);
    	for(int i=k-1;i;--i) inv[i]=1ll*inv[i+1]*(i+1)%p;
    	for(int i=0;i<=k;++i){
    		f[i]=(i&1?-inv[i]+p:inv[i])%p;
    		g[i]=1ll*qpow(i,k)*inv[i]%p;
    	}
    }
    void ntt(int *y,bool f){
    	for(int i=0;i<n;++i) if(i<rev[i]) std::swap(y[i],y[rev[i]]);
    	for(int m=2,id=1;m<=n;m<<=1,++id){
    		for(int k=0;k<n;k+=m){
    			int w=1,wm=f?wn[id]:nw[id];
    			for(int j=0;j<m>>1;++j){
    				int &a=y[k+j]; int &b=y[k+j+m/2];
    				int u=a%p,t=1ll*w*b%p;
    				a=u+t; if(a>=p) a-=p;
    				b=u-t; if(b<0) b+=p;
    				w=1ll*w*wm%p;
     			}
    		}
    	} int x=qpow(n,p-2);
    	if(!f) for(int i=0;i<n;++i) y[i]=1ll*y[i]*x%p;
    }
    int main(){
    	scanf("%d%d",&m,&k); --m;
    	for(n=1;n<=k+1;n<<=1,++lg); n<<=1;
    	init(); calcw(); ntt(f,1); ntt(g,1);	
    	for(int i=0;i<n;++i) f[i]=1ll*f[i]*g[i]%p;
    	ntt(f,0); int ans=0;
    	for(int i=0;i<=k&&i<=m;++i){
    		ans+=1ll*qpow(2,m-i,cur[i])*f[i]%p;
    		if(ans>=p) ans-=p;
    	} 
    	ans=1ll*ans*qpow(2,1ll*m*(m-1)/2,m+1)%p;
    	printf("%d",ans);
    }
    
  • 相关阅读:
    没事千万不要升级VirtualBox和Vagrant!
    以太坊开源项目
    以太坊合约第三方库
    Redis dbfilename
    SUSE Redis 开机启动
    SUSE Linux Enterprise Server 12 SP3 安装redis 6.0以上版本
    HTML基础练习
    html基础
    关于css 浮动的例子
    css 实现三角形
  • 原文地址:https://www.cnblogs.com/enzymii/p/8908291.html
Copyright © 2020-2023  润新知