[集训队互测2012]calc——DP
Posted joker-yza
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[集训队互测2012]calc——DP相关的知识,希望对你有一定的参考价值。
题面
解析
可以强制让$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; }
以上是关于[集训队互测2012]calc——DP的主要内容,如果未能解决你的问题,请参考以下文章
P4463 [集训队互测2012] calc 拉格朗日插值 dp 多项式分析