模拟赛 斯特林数计数 题解(拉格朗日插值)

题目大意:

答案对\(10^9 + 7\)取模。

首先,我们发现这个东西出现了很多次。
设R=

R可以矩阵乘法求。
根据斯特林数的

原式可化为
设所求为\(f_k(N)\)

对于\(R=1\)的情况,自然数幂求和即可。\(O(k+logn)\)

所以,

由上述暴力展开的情况可以发现,存在一个关于\(N\)\(m\)多项式\(F_m(N)\)
使得\(f_m(N)=F_m(N)R^{N+1}-F_m(0)R\),利用上述的展开方式也可归纳证明这个结论。

所以,求出\(F_m(N)\)即可,由于它是\(m\)次多项式,所以,求出\(F_m(0)到F_m(m)\)即可插值求出\(F_m(N)\)
因为\(f_m(N)-f_m(N-1)=F_m(N)R^{N+1}-F_m(N-1)R^N=N^m R^N\),所以\(F_m(N)R-F_m(N-1)=N^m\)
\(F_m(N)=\frac{N^m+F_m(N-1)}{R}\)
\(F_m(0)=x\),那么\(F_m(i)\)就能表示为\(k_ix+b_i\)的形式。
那么,我们只要再找一条\(F_m\)的等量关系即可求出。

代入到上一条式子,即可得出

(其实就是说m次多项式的m+1次差分等于0,这条式子可以保证\(F_m\)形成一个m次多项式)。
也可以这样理解:我们用\(m+1\)个点(0~m)就能确定一个多项式,但要保证确定的多项式代入\(m+1\)的值是正确的。
通过拉格朗日插值也能得出这条式子。

这样,我们就能求出\(F_m(0)到F_m(m)\),再通过插值,即可求出\(F_m(N)\),进而求出答案。

通过线性筛,可以在计算质数\(p\)\(p^m\)后线性地算出所有正整数\(x\)\(x^m\)
时间复杂度为\(O(\frac{m}{ln(m)}*log_2(m))=O(m)\)

总时间复杂度\(O(k+logn)\)

代码:

#include <stdio.h>
#define md 1000000007
#define ll long long
int ny[200010],jc[200010],jn[200010];
int C(int n,int m)
{
	return 1ll*jc[n]*jn[m]%md*jn[n-m]%md;
}
int ksm(int a,int b)
{
	int jg=1;
	while(b>0)
	{
		if(b&1)
			jg=1ll*jg*a%md;
		a=1ll*a*a%md;
		b=(b>>1);
	}
	return jg;
}
int getfib(ll x)
{
	int a=1,b=0,c=0,d=1,w[70],s=0;
	while(x>0)
	{
		w[s++]=x%2;
		x/=2;
	}
	for(int i=s-1;i>=0;i--)
	{
		int bc=1ll*b*c%md,ad=(a+d)%md;
		b=1ll*b*ad%md;c=1ll*c*ad%md;
		a=(1ll*a*a+bc)%md;
		d=(1ll*d*d+bc)%md;
		if(w[i])
		{
			int ta=a,tc=c;
			a=(a+b)%md;
			c=(c+d)%md;
			b=ta;d=tc;
		}
	}
	return c;
}
int qz[200010],hz[200010];
int jisuan(int sz[200010],int n,ll x)
{
	for(int i=0;i<=n;i++)
	{
		if(i==0)qz[i]=1;
		else qz[i]=qz[i-1];
		qz[i]=1ll*qz[i]*(x%md-i+md)%md;
	}
	for(int i=n;i>=0;i--)
	{
		if(i==n)hz[i]=1;
		else hz[i]=hz[i+1];
		hz[i]=1ll*hz[i]*(x%md-i+md)%md;
	}
	int ans=0;
	for(int i=0;i<=n;i++)
	{
		int l=1ll*jn[i]*jn[n-i]%md;
		if(i>0)l=1ll*l*qz[i-1]%md;
		if(i<n)l=1ll*l*hz[i+1]%md;
		if((n-i)%2==1)
			l=1ll*l*(md-1)%md;
		ans=(ans+1ll*l*sz[i])%md;
	}
	return ans;
}
int mi[200010],k[200010],b[200010],S[200010],F[200010];
int sa[200010],ss[200010],sl=0;
void yucl(int x,int m)
{
	mi[1]=1;
	for(int i=2;i<=x;i++)
	{
		if(!sa[i])
		{
			ss[sl++]=i;
			mi[i]=ksm(i,m);
		}
		for(int j=0;j<sl;j++)
		{
			if(1ll*i*ss[j]>x)
				break;
			sa[i*ss[j]]=true;
			mi[i*ss[j]]=1ll*mi[i]*mi[ss[j]]%md;
			if(i%ss[j]==0)break;
		}
	}
}
int main()
{
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	ll n,r;int K;
	scanf("%lld%lld%d",&n,&r,&K);
	ny[1]=jc[0]=jn[0]=1;
	for(int i=2;i<=K+1;i++)
		ny[i]=1ll*(md-md/i)*ny[md%i]%md;
	for(int i=1;i<=K+1;i++)
	{
		jc[i]=1ll*jc[i-1]*i%md;
		jn[i]=1ll*jn[i-1]*ny[i]%md;
	}
	yucl(K+1,K);
	int R=(getfib(r+2)-1+md)%md;
	if(R==1)
	{
		for(int i=1;i<=K+1;i++)
			mi[i]=(mi[i]+mi[i-1])%md;
		printf("%d",jisuan(mi,K+1,n));
	}
	else
	{
		int rn=ksm(R,md-2);
		k[0]=1;b[0]=0;
		for(int i=1;i<=K+1;i++)
		{
			k[i]=1ll*k[i-1]*rn%md;
			b[i]=1ll*(b[i-1]+mi[i])*rn%md;
		}
		int hk=0,hb=0;
		for(int i=0;i<=K+1;i++)
		{
			int t=C(K+1,i);
			if(i%2==1)
				t=1ll*t*(md-1)%md;
			hk=(hk+1ll*t*k[i])%md;
			hb=(hb+1ll*t*b[i])%md;
		}
		F[0]=1ll*(md-hb)*ksm(hk,md-2)%md;
		for(int i=1;i<=K;i++)
			F[i]=(1ll*k[i]*F[0]+b[i])%md;
		int fn=jisuan(F,K,n);
		int ans=1ll*fn*ksm(R,(n+1)%(md-1))%md;
		ans=(ans-1ll*F[0]*R%md+md)%md;
		printf("%d\n",ans);
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}
posted @ 2019-10-02 18:57  lnzwz  阅读(321)  评论(0编辑  收藏  举报