设
f
[
i
]
[
j
]
f[i][j]
f[i][j] 表示前
i
i
i 个数选的最大的数
≤
j
\le j
≤j 的贡献和,转移显然为
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
−
1
]
×
j
+
f
[
i
]
[
j
−
1
]
f[i][j] = f[i - 1][j - 1] \times j + f[i][j - 1]
f[i][j]=f[i−1][j−1]×j+f[i][j−1],时间复杂度
O
(
n
A
)
O(nA)
O(nA)。
现在我们尝试证明:
f
[
n
]
[
A
]
f[n][A]
f[n][A] 可以用一个关于
A
A
A 的
2
n
+
1
2n + 1
2n+1 次多项式表示。
这个转移方程似乎不太好弄,我们先转换一下思路。
设
g
[
i
]
[
j
]
g[i][j]
g[i][j] 表示前
i
i
i 个数选的最大的数
=
j
= j
=j 的贡献和,转移显然为
g
[
i
]
[
j
]
=
(
∑
k
=
0
j
−
1
g
[
i
−
1
]
[
k
]
)
×
j
g[i][j] = (\sum \limits_{k = 0}^{j - 1} g[i - 1][k]) \times j
g[i][j]=(k=0∑j−1g[i−1][k])×j,则
f
[
n
]
[
A
]
=
∑
j
=
0
A
g
[
n
]
[
j
]
f[n][A] = \sum \limits_{j = 0}^{A} g[n][j]
f[n][A]=j=0∑Ag[n][j]。
若
g
[
n
]
[
j
]
g[n][j]
g[n][j] 可以用一个关于
j
j
j 的多项式表示,我们对
g
[
n
]
[
j
]
g[n][j]
g[n][j] 求前缀和实际上使多项式次数
+
1
+1
+1,因此现在只要证明
g
[
n
]
[
j
]
g[n][j]
g[n][j] 可以用一个
2
n
2n
2n 次多项式表示。
考虑数学归纳,
n
=
0
n = 0
n=0 时显然成立,每次转移相当于求前缀和并
×
j
(
1
≤
j
≤
A
)
\times j(1 \le j \le A)
×j(1≤j≤A),多项式次数增加
2
2
2,得证。
又因为
f
[
n
]
[
0
]
=
0
f[n][0] = 0
f[n][0]=0,常数项肯定为
0
0
0,用
2
n
+
1
2n + 1
2n+1 个点即可表示这个多项式。
直接暴力拉格朗日插值即可,时间复杂度
O
(
n
2
)
O(n^2)
O(n2)。
Code
#include<algorithm>#include<iostream>#include<cstring>#include<cstdlib>#include<cctype>#include<cstdio>#include<ctime>#include<cmath>template<classT>inlinevoidread(T &res){char ch;while(ch =getchar(),!isdigit(ch));
res = ch ^48;while(ch =getchar(),isdigit(ch))
res = res *10+ ch -48;}constint N =505;constint M =1005;int A, n, mod, m, y[M], f[N][M];inlinevoidadd(int&x,int y){
x += y;
x >= mod ? x -= mod :0;}inlineintquick_pow(int x,int k){int res =1;while(k){if(k &1) res =1ll* res * x % mod;
x =1ll* x * x % mod; k >>=1;}return res;}inlineintLagrange(int x){if(x >=1&& x <= m)return y[x];int res =0;for(int i =1; i <= m;++i){int p = y[i], q =1;for(int j =1; j <= m;++j)if(i != j){
p =1ll* p *(x - j)% mod;
q =1ll* q *(i - j)% mod;}
p <0? p += mod :0;
q <0? q += mod :0;
res =(res +1ll* p *quick_pow(q, mod -2))% mod;}return res;}intmain(){read(A);read(n);read(mod);int res =1;for(int i =1; i <= n;++i)
res =1ll* res * i % mod;
m = n + n +1;for(int j =0; j <= m;++j)
f[0][j]=1;for(int i =1; i <= n;++i)for(int j =1; j <= m;++j)
f[i][j]=(1ll* f[i -1][j -1]* j + f[i][j -1])% mod;for(int j =1; j <= m;++j)
y[j]= f[n][j];
std::cout <<1ll* res *Lagrange(A)% mod << std::endl;}