Description
一个序列 a1,a2,…,an 是合法的,当且仅当:
- a1,a2,…,an 都是 [1,k] 中的整数。
- a1,a2,…,an 互不相等。
一个序列的值定义为它里面所有数的乘积,即 a1×a2×⋯×an。
求所有不同合法序列的值的和对 p 取模后的结果。两个序列不同当且仅当他们任意一位不同。
k≤109,n≤500,p≤109,保证 p 为素数,保证 n+1<k<p。
Solution
先考虑一个暴力 dp。
设 fi,j 表示前 i 个数,值域为 [1,j] 且这些数单调递增的总和。
那么可以得到转移:fi,j=fi,j−1+j×fi−1,j−1。
时间复杂度:O(nk)。
考虑优化。
可以猜测 fi,j 是关于 j 的某个多项式,且对于第一维相同的次数相同。
不妨设 gi 表示 fi 的系数。
那么把 fi 做差分后的多项式系数为原来的系数 −1,即 fi,j−fi,j−1=j×fi−1,j−1 的系数为 gi−1。
所以 gi−1=gi−1+1,也就是 gi=gi−1+2。
注意到 g0 一定是 0,所以 gi=2i。
然后只要求出 fn,1,fn,2,…,fn,2n+1,然后做一遍拉格朗日插值即可。
时间复杂度:O(n2)。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include <bits/stdc++.h>
#define int int64_t
const int kMaxN = 505;
int k, n, mod; int f[kMaxN][kMaxN * 2];
int qpow(int bs, int idx = mod - 2) { int ret = 1; for (; idx; idx >>= 1, bs = 1ll * bs * bs % mod) if (idx & 1) ret = 1ll * ret * bs % mod; return ret; }
void dickdreamer() { std::cin >> k >> n >> mod; std::fill_n(f[0], std::min(2 * n + 1, k) + 1, 1); for (int i = 1; i <= n; ++i) { for (int j = 1; j <= std::min(2 * n + 1, k); ++j) f[i][j] = (f[i][j - 1] + 1ll * j * f[i - 1][j - 1] % mod) % mod; } int ans = 0; for (int i = 1; i <= 2 * n + 1; ++i) { int tmpu = f[n][i], tmpd = 1; for (int j = 1; j <= 2 * n + 1; ++j) if (i != j) tmpu = 1ll * tmpu * ((k - j + mod) % mod) % mod, tmpd = 1ll * tmpd * ((i - j + mod) % mod) % mod; ans = (ans + 1ll * tmpu * qpow(tmpd) % mod) % mod; } for (int i = 1; i <= n; ++i) ans = 1ll * ans * i % mod; std::cout << ans << '\n'; }
int32_t main() { #ifdef ORZXKR freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0); int T = 1; while (T--) dickdreamer(); return 0; }
|