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;
}