考慮 dp 求解,dp 構造升序序列,最後乘上
令 表示前 個位置,最大值小於等於 的貢獻,轉移方程:
最終答案是 ,k 非常大肯定無法求解,考慮優化:
令 表示前 個位置,第 個放 的貢獻,轉移方程:
顯然有
如果可以證明 是一個以 爲自變量的多項式,就可以使用拉格朗日插值快速求解
使用歸納法證明:
當 n = 0 時,g[0][k] = 0,結論成立
設 n > 0 且,g[n][k] 是一個以 k 爲自變量的多項式
根據轉移方程,有:,根據k次冪和的推論,可以得知 是以 爲自變量的多項式,當 時 是一個 次多項式, 每增一,根據轉移方程可以得出 多項式的次數 ,因此 是一個以 爲自變量的 次多項式。
因此 是一個以 k 爲自變量的 次多項式,求出 個點,通過插值快速求出最終答案。不要忘了乘上
代碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e3 + 10;
typedef long long ll;
int mod,mx,n,k;
ll fac[maxn],ifac[maxn];
inline ll add(ll x, ll y) {
x += y;
if (x >= mod) x -= mod;
return x;
}
inline ll sub(ll x, ll y) {
x -= y;
if (x < 0) x += mod;
return x;
}
inline ll mul(ll x, ll y) {
return x * y % mod;
}
ll fpow(ll a,ll b) {
ll r = 1;
while(b) {
if (b & 1) r = mul(r,a);
b >>= 1;
a = mul(a,a);
}
return r;
}
ll cal(ll g[maxn],ll x) { //拉格朗日插值計算多項式
if (x <= mx) return g[x];
ll tmp = 1,inv,ans = 0;
for (int i = 1; i <= mx; i++)
tmp = mul(tmp,x - i);
for (int i = 1; i <= mx; i++) {
ll res = 1, inv = fpow(x - i,mod - 2);
res = mul(res,g[i]);
res = mul(res,ifac[i - 1]);
res = mul(res,ifac[mx - i]);
res = mul(res,inv);
res = mul(res,tmp);
if ((mx - i) & 1) res = mul(res,-1);
if (res < 0) res += mod;
ans = add(ans,res);
}
return ans;
}
ll f[maxn],tp[maxn],dp[2000][2000];
int main() {
scanf("%d%d%d",&k,&n,&mod);
fac[0] = 1;
for (int i = 1; i <= 4000; i++)
fac[i] = mul(fac[i - 1],i);
ifac[4000] = fpow(fac[4000],mod - 2);
for (int i = 4000 - 1; i >= 0; i--)
ifac[i] = mul(ifac[i + 1],i + 1);
mx = 2 * n + 4;
for (int j = 0; j <= mx; j++)
dp[0][j] = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= mx; j++)
dp[i][j] = (1ll * dp[i - 1][j - 1] * j + dp[i][j - 1]) % mod;
printf("%lld\n",1ll * cal(dp[n],k) * fac[n] % mod);
return 0;
}