luogu P4463 [集训队互测 2012] calc
题面传送门
我们设\(dp_{i,j}\)为到了第\(i\)个数,当前数为\(j\)的答案。
那么最后答案为\(\sum\limits_{i=1}^{k}{dp_{n,i}}\)
然后这个转移方程显然是\(dp_{i,j}=\sum\limits_{k=1}^{j-1}{dp_{i-1}[k}\times j\)
看上去这个东西可以拉格朗日插值。
每次前缀和使次数加一,\(\times j\)使次数加一,所以是\(2n+1\)次。
时间复杂度\(O(n^2)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 500
#define M 200000
#define mod 998244353
#define eps (1e-7)
#define U unsigned int
#define IT set<ques>::iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
using namespace std;
int n,k,p,m;ll dp[N+5][N+5<<1],ans,now,pus,frc=1;
I ll mpow(ll x,int y=p-2){ll ans=1;while(y) (y&1)&&(ans=ans*x%p),y>>=1,x=x*x%p;return ans;}
I ll calc(int k){
re int i,j;for(i=0;i<=m;i++){
now=pus=1;for(j=0;j<=m;j++) (i^j)&&(now=now*(k-j)%p,pus=pus*(i-j)%p);
ans+=dp[n][i]*now%p*mpow(pus)%p;
}return (ans%p+p)%p;
}
int main(){
// freopen("1.in","r",stdin);
re int i,j;scanf("%d%d%d",&k,&n,&p);m=2*n;for(i=0;i<=m;i++)dp[0][i]=1;for(i=1;i<=n;i++) frc=frc*i%p;
for(i=1;i<=n;i++){
for(j=1;j<=m;j++) dp[i][j]=(dp[i-1][j-1]*j+dp[i][j-1])%p;
}printf("%lld\n",calc(k)*frc%p);
}