题面传送门
我们设\(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); }