[BZOJ2655]calc
试题描述
一个序列 \(a_1, \cdots , a_n\) 是合法的,当且仅当:
长度为给定的 \(n\)。
\(a_1, \cdots , a_n\) 都是 \([1,A]\) 中的整数。
\(a_1, \cdots , a_n\) 互不相等。
一个序列的值定义为它里面所有数的乘积,即 \(a_1a_2 \cdots a_n\)。
求所有不同合法序列的值的和。
两个序列不同当且仅当他们任意一位不一样。
输出答案对一个数 \(mod\) 取余的结果。
输入
一行 \(3\) 个数,\(A\),\(n\),\(mod\)。意义为上面所说的。
输出
一行结果。
输入示例
9 7 10007
输出示例
3611
数据规模及约定
\(0: A \le 10,n \le 10\)。
\(1 \sim 3: A \le 1000,n \le20\).
\(4 \sim 9: A \le 10^9,n \le 20\)
\(10 \sim 19: A \le 10^9,n \le 500\)。
全部:\(mod \le 10^9\),并且 \(mod\) 为素数,\(mod>A>n+1\)
题解
令 \(f(i, j)\) 表示 \([1, i]\) 中的数取 \(j\) 个的所有方案的乘积总和,那么转移如下
\[ f(i, j) = f(i-1, j-1) \cdot i \cdot j + f(i-1, j) \]
边界条件是 \(f(i, i) = (i!)^2, f(a, b) = 0 (a > b)\),那么我们可以归纳地用上面的递推式证明 \(f(i, j)\) 可以表示为一个关于 \(i\) 的 \(2j\) 次多项式。首先 \(f(i, i)\) 就是 \(2i\) 次多项式,然后那个转移中 \(f(i-1, j-1) \cdot i \cdot j\) 是 \(2j - 1\) 次的,\(f(i-1, j)\) 是 \(2j\) 次的,两个相加就是 \(2j\) 次的。
于是我们 dp 出较小的值,然后拉格朗日插值一下就能得到 \(f(A, n)\) 关于 \(A\) 的多项式了,从而得到答案。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
return x * f;
}
#define maxn 510
#define LL long long
int Arrange, n, MOD, f[maxn*3][maxn], inv[maxn*3];
int X[maxn<<1], Y[maxn<<1];
int GetPoly(int N, int x) {
int ans = 0;
rep(i, 1, N) {
int sum = Y[i];
rep(j, 1, N) if(i != j) {
sum = (LL)sum * (x - X[j]) % MOD * inv[abs(X[i]-X[j])] % MOD;
if(sum < 0) sum += MOD;
if(X[i] < X[j] && sum) sum = MOD - sum;
}
ans += sum;
if(ans >= MOD) ans -= MOD;
}
return ans;
}
int main() {
Arrange = read(); n = read(); MOD = read();
int lim = min(Arrange, n * 3 + 1);
rep(i, 0, lim) f[i][0] = 1;
rep(i, 1, lim) rep(j, 1, n) {
f[i][j] = (LL)f[i-1][j-1] * i % MOD * j % MOD + f[i-1][j];
if(f[i][j] >= MOD) f[i][j] -= MOD;
}
if(Arrange == lim) return printf("%d\n", f[lim][n]), 0;
rep(i, n + 1, lim) X[i-n] = i, Y[i-n] = f[i][n];
inv[1] = 1;
rep(i, 2, lim) inv[i] = (LL)(MOD - MOD / i) * inv[MOD%i] % MOD;
printf("%d\n", GetPoly(n << 1 | 1, Arrange));
return 0;
}