[HEOI2016/TJOI2016]求和

前置:第二类斯特林数

表示把\(n\)个小球放入\(m\)个不可区分的盒子的方案数

使用容斥原理分析,假设盒子可区分枚举至少有几个盒子为空,得到通项:

\[S(n,m)=\frac{1}{m!}\sum_{k=0}^{m}(-1)^k\binom{m}{k}(m-k)^n \]

分析

\[f(n)=\sum_{i=0}^n\sum_{j=0}^iS(i,j)2^jj! \]

注意到\(S(n,m)=0\quad(m>n)\),因此第二个求和上限可改为\(n\),并代入第二类斯特林数的通项,得到

\[f(n)=\sum_{i=0}^n\sum_{j=0}^n\frac{1}{j!}\sum_{k=0}^j(-1)^k\frac{j!}{k!(j-k)!}(j-k)^i2^jj! \]

\[=\sum_{j=0}^n2^jj!\sum_{k=0}^{j}\frac{(-1)^k}{k!(j-k)!}\sum_{i=0}^n(j-k)^i \]

\[=\sum_{j=0}^n2^jj!\sum_{k=0}^{j}\frac{(-1)^k}{k!}\frac{\sum_{i=0}^n(j-k)^i}{(j-k)!} \]

\(g(j)=\sum_{k=0}^{j}\frac{(-1)^k}{k!}\frac{\sum_{i=0}^n(j-k)^i}{(j-k)!}\),则\(f(n)=\sum_{j=0}^n2^jj!g(j)\)

\(a_k=\frac{(-1)^k}{k!},b_k=\frac{\sum_{i=0}^nk^i}{k!}=\frac{k^{n+1}-1}{(k-1)k!}\quad (k>1)\)
\(g=a \otimes b\)
然后先求出\(g\),再求出\(f\)

代码

#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<complex>
#pragma GCC optimize ("O3")
#define rg register
using namespace std;
template<class T> inline T read(T&x){
    T data=0;
	int w=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
		if(ch=='-')
			w=-1;
		ch=getchar();
	}
    while(isdigit(ch))
        data=10*data+ch-'0',ch=getchar();
    return x=data*w;
}
typedef long long ll;

const int mod=998244353,g=3;

int rev[1<<18|7];

inline void calrev(int lim,int l)
{
	rev[0]=0;
	for(int i=1;i<lim;++i)
		rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
}

inline int qpow(int x,int k)
{
	int ans=1;
	while(k)
	{
		if(k&1)
			ans=(ll)ans*x%mod;
		x=(ll)x*x%mod,k>>=1;
	}
	return ans;
}

inline void FFT(int*t,int lim,int type)
{
	for(rg int i=0;i<lim;++i)
		if(i<rev[i])
			swap(t[i],t[rev[i]]);
	for(rg int i=1;i<lim;i<<=1)
	{
		int gn=qpow(g,(mod-1)/(i<<1));
		if(type==-1)
			gn=qpow(gn,mod-2);
		for(rg int j=0;j<lim;j+=(i<<1))
		{
			int gi=1;
			for(rg int k=0;k<i;++k,gi=(ll)gi*gn%mod)
			{
				int x=t[j+k],y=(ll)gi*t[j+i+k]%mod;
				t[j+k]=x+y,t[j+i+k]=x-y+mod;
				if(t[j+k]>=mod)
					t[j+k]-=mod;
				if(t[j+i+k]>=mod)
					t[j+i+k]-=mod;
			}
		}
	}
	if(type==-1)
	{
		int inv=qpow(lim,mod-2);
		for(rg int i=0;i<lim;++i)
			t[i]=(ll)t[i]*inv%mod;
	}
}

int fac[100010],inv[100010],pow2[100010];

int a[1<<18|7],b[1<<18|7];

int main()
{
	int n;
	read(n);
	fac[0]=inv[0]=pow2[0]=a[0]=b[0]=1;
	for(rg int i=1;i<=n;++i)
	{
		fac[i]=(ll)fac[i-1]*i%mod;
		inv[i]=qpow(fac[i],mod-2);
		pow2[i]=pow2[i-1]<<1;
		if(pow2[i]>=mod)
			pow2[i]-=mod;
		a[i]=inv[i];
		if(i&1)
			a[i]=mod-a[i];
		b[i]=(ll)(qpow(i,n+1)-1)%mod*inv[i]%mod*qpow(i-1,mod-2)%mod;
	}
	b[1]=n+1; 
	int lim=1,l=0;
	while(lim<=n*2) 
		lim<<=1,++l;
	calrev(lim,l);
	FFT(a,lim,1);
	FFT(b,lim,1);
	for(rg int i=0;i<lim;++i)
		a[i]=(ll)a[i]*b[i]%mod;
	FFT(a,lim,-1);
	int f=0;
	for(rg int i=0;i<=n;++i)
	{
		f+=(ll)pow2[i]*fac[i]%mod*a[i]%mod;
		if(f>=mod)
			f-=mod;
	}
	printf("%d\n",f);
    return 0;
}


Hint

b1不能用等比数列公式计算,必须单独处理。

我只需要n项,但是只算到n项是错的,举例答案是3次函数,我只需要2项就只求求2项,算出来就是一个1次函数,就是错的

总结:lim必须至少是待卷积式子长度的两倍

posted on 2018-08-22 21:42  autoint  阅读(130)  评论(0编辑  收藏  举报

导航