[国家集训队] calc

题目

显然有一个暴力\(dp\)

\(dp_{i,j}\)表示前\(i\)个数选了\(j\)个的答案

转移显然

\[dp_{i,j}=dp_{i-1,j}+dp_{i-1,j-1}\times i \]

由于元素是有顺序的,答案是\(dp_{A,n}\times n!\)

复杂度是\(O(nA)\)的显然过不了

我们观察一下转移,简单一个移项

\[dp_{i,j}-dp_{i-1,j}=dp_{i-1,j-1}\times i \]

我们如果把\(dp_{i,j}\)视为一个与\(i\)有关的多项式,那么\(dp_{i,j}-dp_{i-1,j}\)就是做了一个差分,发现得到的还是一个多项式,而且是一个次数比\(dp_{j-1}\)高一次的多项式

很显然\(dp_{j}\)就比\(dp_{j-1}\)高了两次,\(dp_{0}\)是一个\(0\)次多项式,由此可以\(dp_{j}\)是一个\(2j\)次多项式

于是对于\(dp_{A,n}\)就是一个\(2n\)次多项式,我们暴力dp出\(2n\)个取值之后大力拉格朗日插值就好了

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
const int maxn=1005;
int dp[maxn][505],n,m,mod;
inline int ksm(int a,int b) {
	int S=1;
	for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) S=1ll*S*a%mod;
	return S;
}
int main() {
	scanf("%d%d%d",&m,&n,&mod);dp[0][0]=1;
	for(re int i=1;i<=2*n+1;i++) {
		dp[i][0]=dp[i-1][0];
		for(re int j=1;j<=i;j++) 
			dp[i][j]=(dp[i-1][j]+1ll*dp[i-1][j-1]*i%mod)%mod;
	}
	int ans=0;
	for(re int i=1;i<=2*n+1;i++) {
		int q=1,p=1;
		for(re int j=1;j<=2*n+1;j++)
		if(i!=j) p=1ll*(m-j)*p%mod,q=1ll*(i-j)*q%mod;
		q=(q+mod)%mod,p=(p+mod)%mod;
		ans=(ans+1ll*dp[i][n]*p%mod*ksm(q,mod-2)%mod)%mod;
	}
	for(re int i=1;i<=n;i++) ans=1ll*ans*i%mod;
	printf("%d\n",ans);
	return 0;
}
posted @ 2019-05-26 19:21  asuldb  阅读(97)  评论(0编辑  收藏  举报