bzoj2655 calc [拉格朗日插值]

29 篇文章 0 订阅
2 篇文章 0 订阅

Description:
一个序列 a1,...,an a 1 , . . . , a n 是合法的,当且仅当:
长度为给定的 n n
a1,...,an都是 [1,A] [ 1 , A ] 中的整数。
a1,...,an a 1 , . . . , a n 互不相等。
一个序列的值定义为它里面所有数的乘积,即 a1a2...an a 1 a 2 . . . a n
求所有不同合法序列的值的和。
两个序列不同当且仅当他们任意一位不一样。
输出答案对一个数 mod m o d 取余的结果。


Solution:
考虑一个暴力的 dp,dp[i][j] d p , d p [ i ] [ j ] 表示前 i i 个数选j个数。
那么转移即为 dp[i][j]=dp[i1][j]+dp[i1][j1]ij d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − 1 ] ∗ i ∗ j 表示选或不选,并且枚举插入的位置。
由于这个 dp d p 式是一个 2n 2 ∗ n 的多项式,所以我们插值求第 A A 项即可。
注意插值如果从1开始要插到 2n+1 2 ∗ 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[n][A]);
    } else {
        printf("%lld\n", calc(dp[n], A, 2 * n));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值