BZOJ 2655: calc(拉格朗日插值)

传送门

解题思路

  首先比较容易能想到\(dp\),设\(f[i][j]\)表示前\(j\)个数,每个数\(<=i\)的答案,那么有转移方程:\(f[i][j]=f[i-1][j-1]*i*j+f[i-1][j]\)。这个转移复杂度是\(O(n*A)\)的,无法通过此题。考虑优化,打个表发现这其实是一个多项式,次数可以用差分法确定,然后用拉格朗日插值即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;
const int MAXN = 505;
typedef long long LL;

int n,A,MOD;
LL f[MAXN<<2][MAXN<<2],ans;

inline int fast_pow(int x,int y){
	int ret=1;
	for(;y;y>>=1){
		if(y&1) ret=(LL)ret*x%MOD;
		x=(LL)x*x%MOD;
	}	
	return ret;
}

signed main(){
	scanf("%d%d%d",&A,&n,&MOD);
	f[0][0]=1;
	for(int i=1;i<=n*3;i++){
		f[i][0]=f[i-1][0];
		for(int j=1;j<=i;j++){
			f[i][j]=(LL)f[i-1][j-1]*i%MOD*j%MOD+f[i-1][j];
			f[i][j]%=MOD;	
		}
	}
	LL s1,s2;
	if(A<=n*3) {printf("%lld\n",f[A][n]);return 0;}
	for(int i=n;i<=n*3;i++){
		s1=s2=1ll;
		for(int j=n;j<=n*3;j++)if(i!=j){
			s1=s1*(A-j)%MOD;
			s2=s2*(i-j)%MOD;
		}
		s1=(s1+MOD)%MOD;s2=(s2+MOD)%MOD;
		ans=ans+s1%MOD*fast_pow(s2,MOD-2)%MOD*f[i][n]%MOD;ans%=MOD;
	}
	printf("%lld",ans);
	return 0;	
}
posted @ 2018-12-05 09:18  Monster_Qi  阅读(163)  评论(0编辑  收藏  举报