Description
给出 (A,n,p) ,让你在模 (p) 意义下求所有序列 (a) 满足“长度为 (n) 且 (a_iin[1,A]) ,并且对于 (i eq j,a_i eq a_j)”的价值和。
一个序列的价值定义为 (prodlimits_{i=1}^n a_i) 。
(1leq Aleq 10^9,1leq nleq 500,pleq 10^9) 并且 (p) 为素数, (p>A>n+1) 。
Solution
考虑朴素的 ( ext{DP}) 。
记 (f_{i,j}) 为长度为 (j) 的序列只含 ([1,i]) 内的数的价值。
转移是考虑是否加上 (i) 这个元素并且如果加上放在哪一位,那么
[f_{i,j}=f_{i-1,j-1} imes j imes i+f_{i-1,j}]
这样转移是 (O(An)) 的,过不了...
据说 (f_{i,j}) 是一个关于 (i) 有关的 (2j) 次的多项式。
具体证明的话,可以差分之后用数学归纳法来证。
那么我们考虑 ( ext{DP}) 出 (f_{i,n},iin[0,2n]) 的值,用拉格朗日插值求出唯一解即可...
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1000+5;
int f[N][N], a, n, p, x[N], y[N], ifac[N], inv[N];
int quick_pow(int a, int b) {
int ans = 1;
while (b) {
if (b&1) ans = 1ll*ans*a%p;
b >>= 1, a = 1ll*a*a%p;
}
return ans;
}
int lagrange(int n, int *x, int *y, int xi) {
int ans = 0, s1 = 1;
for (int i = 0; i <= n; i++) {
s1 = 1ll*s1*(xi-x[i])%p;
inv[i] = quick_pow(xi-x[i], p-2);
}
ifac[1] = ifac[0] = 1;
for (int i = 2; i <= n; i++) ifac[i] = -1ll*p/i*ifac[p%i]%p;
for (int i = 2; i <= n; i++) ifac[i] = 1ll*ifac[i]*ifac[i-1]%p;
for (int i = 0; i <= n; i++)
(ans += 1ll*y[i]*s1%p*inv[i]%p*ifac[i]%p*(((n-i)&1) ? -1 : 1)*ifac[n-i]%p) %= p;
return (ans+p)%p;
}
void work() {
scanf("%d%d%d", &a, &n, &p);
for (int i = 0; i <= n*2; i++) f[i][0] = 1;
for (int i = 1; i <= n*2; i++)
for (int j = 1; j <= n; j++)
f[i][j] = (1ll*f[i-1][j-1]*i%p*j%p+f[i-1][j])%p;
if (a <= 2*n) {printf("%d
", f[a][n]); return; }
for (int i = 0; i <= 2*n; i++) x[i] = i, y[i] = f[i][n];
printf("%d
", lagrange(2*n, x, y, a));
}
int main() {work(); return 0; }