[BZOJ2655]calc

Posted xjr_01

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ2655]calc相关的知识,希望对你有一定的参考价值。

[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;
}

以上是关于[BZOJ2655]calc的主要内容,如果未能解决你的问题,请参考以下文章

Bzoj2655 calc

bzoj2655calc 容斥+dp

calc BZOJ 2655

bzoj[2655] calc

BZOJ2655: calc 动态规划+拉格朗日插值

[集训队互测2012]calc——DP