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

题面

  Bzoj2655

解析

  可以强制让$a$数列递增,最后乘以$n!$

  有一个显然的$dp$,$f[i][j]$表示填前$i$个位置,且填的数最大不超过$j$的序列权值和,易有:$f[i][j] = f[i][j-1] + f[i-1][j-1] * j$

  $O(AN)$的$dp$显然会$T$

  设$f[i][j]$是关于$j$的$g(i)$次多项式,$g(0)=0$

  注意到$f[i][j] - f[i][j-1]$就是关于$j$的$g(i)-1$次多项式,将$dp$方程移项:$f[i][j] - f[i][j-1] = f[i-1][j-1] * j$,于是有:$$g(i)-1 = g(i-1)+1$$$$g(i)=2i$$

  那么可以用$dp$算出$f[n][1cdots 2*n+1]$,再用拉格朗日插值求出$f[n][A]$

  拉插公式:$$F(x)=sum_{i=1}^n y_i prod_{j=1, j eq i}^n frac{x-x_j}{x_i-x_j}$$

  $O(N^2)$

 代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 1005;

int mod;

int add(int x, int y)
{
    return x + y < mod? x + y: x + y - mod;
}

int rdc(int x, int y)
{
    return x - y < 0? x - y + mod: x - y;
}

ll qpow(ll x, int y)
{
    ll ret = 1;
    while(y)
    {
        if(y&1)
            ret = ret * x % mod;
        x = x * x % mod;
        y >>= 1;
    }
    return ret;
}

int m, n;
ll dp[maxn][maxn];

int main()
{
    scanf("%d%d%d", &m, &n, &mod);
    for(int i = 1; i <= 2 * n + 1; ++i)
        dp[1][i] = add(dp[1][i-1], i);
    for(int i = 2; i <= n; ++i)
        for(int j = 1; j <= 2 * n + 1; ++j)
            dp[i][j] = add(dp[i][j-1], dp[i-1][j-1] * j % mod);
    ll ans = 0, mul1, mul2;
    for(int i = 1; i <= 2 * n + 1; ++i)
    {
        mul1 = dp[n][i];
        mul2 = 1;
        for(int j = 1; j <= 2 * n + 1; ++j)
            if(i != j)
            {
                mul1 = mul1 * rdc(m, j) % mod;
                mul2 = mul2 * rdc(i, j) % mod;
            }
        ans = add(ans, mul1 * qpow(mul2, mod - 2) % mod);
    }
    for(int i = 1; i <= n; ++i)
        ans = ans * i % mod;
    printf("%lld", ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Joker-Yza/p/12642649.html