[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
", 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
", GetPoly(n << 1 | 1, Arrange));
return 0;
}