您的位置:首页 > 其它

bzoj2655 calc [拉格朗日插值]

2018-03-27 22:26 204 查看
Description:

一个序列a1,...,ana1,...,an是合法的,当且仅当:

长度为给定的nn。

a1,...,ana1,...,an都是[1,A][1,A]中的整数。

a1,...,ana1,...,an互不相等。

一个序列的值定义为它里面所有数的乘积,即a1a2...ana1a2...an。

求所有不同合法序列的值的和。

两个序列不同当且仅当他们任意一位不一样。

输出答案对一个数modmod取余的结果。

Solution:

考虑一个暴力的dp,dp[i][j]dp,dp[i][j]表示前ii个数选jj个数。

那么转移即为dp[i][j]=dp[i−1][j]+dp[i−1][j−1]∗i∗jdp[i][j]=dp[i−1][j]+dp[i−1][j−1]∗i∗j 表示选或不选,并且枚举插入的位置。

由于这个dpdp式是一个2∗n2∗n的多项式,所以我们插值求第AA项即可。

注意插值如果从11开始要插到2∗n+12∗n+1次。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1005;
int n, A, P;
ll pre[maxn], suf[maxn], inv[maxn], dp[maxn][maxn];
ll calc(ll *f, ll u, int n) {
pre[0] = 1;
suf[n + 2] = 1;
for(int i = 1; i <= n + 1; ++i) {
pre[i] = pre[i - 1] * (u - i + P) % P;
}
for(int i = n + 1; i; --i) {
suf[i] = suf[i + 1] * (u - i + P) % P;
}
ll ret = 0, tmp;
for(int i = 1; i <= n + 1; ++i) {
tmp = f[i] * pre[i - 1] % P * suf[i + 1] % P * inv[i - 1] % P * inv[n - i + 1] % P;
if((n - i + 1) & 1) {
tmp = P - tmp;
}
ret = (ret + tmp) % P;
}
return ret;
}
int main() {
scanf("%d%d%d", &A, &n, &P);
inv[0] = inv[1] = 1;
for(int i = 2; i < maxn; ++i) {
inv[i] = (P - P / i) * inv[P % i] % P;
}
for(int i = 2; i < maxn; ++i) {
inv[i] = inv[i] * inv[i - 1] % P;
}
dp[0][0] = 1;
for(int j = 1; j <= 2 * n + 1; ++j) {
for(int i = 0; i <= n; ++i) {
dp[i][j] = (dp[i][j] + dp[i][j - 1]) % P;
if(i) {
dp[i][j] = (dp[i][j] + dp[i - 1][j - 1] * i % P * j % P) % P;
}
}
}
if(A <= 2 * n) {
printf("%lld\n", dp
[A]);
} else {
printf("%lld\n", calc(dp
, A, 2 * n));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: