bzoj2655 calc [拉格朗日插值]

本文介绍了一种求解特定条件下所有合法序列值之和的算法。合法序列需满足长度固定、元素唯一且来自指定范围的整数集合。通过动态规划方法计算所有可能序列的值并求和,最终结果对特定数取模。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


Solution:
考虑一个暴力的dp,dp[i][j]dp,dp[i][j]表示前ii个数选j个数。
那么转移即为dp[i][j]=dp[i1][j]+dp[i1][j1]ijdp[i][j]=dp[i−1][j]+dp[i−1][j−1]∗i∗j 表示选或不选,并且枚举插入的位置。
由于这个dpdp式是一个2n2∗n的多项式,所以我们插值求第AA项即可。
注意插值如果从1开始要插到2n+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[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、付费专栏及课程。

余额充值