拉格朗日插值优化DP

模拟赛出现神秘插值,太难啦!!

回忆拉格朗日插值是用来做什么的

对于一个多项式\(F(x)\),如果已知它的次数为\(m - 1\),且已知\(m\)个点值,那么可以得到

\[F(k) = \sum_{i=1}^{m} y_i \prod_{i \neq j} \frac{k-x_j}{x_i - x_j}
\]

所以,如果我们知道要求的东西是一个次数比较友好的多项式且容易求出一些点值,那么就可以把答案插出来。

来看两道例题

CF995F Cowmpany Cowmpensation

题意:给你一棵树,要求给每个点分配\([1,d]\)内的权值,且儿子的权值不能超过父亲的权值,对\(10^9+7\)取模,\(D\leq 10^9\)

很容易得到一个\(\text{DP}\),设\(f_{u,i}\)表示u子树内u的权值大于等于\(i\)的答案,那么

\[f_{u,i} = \prod_v f_{v,i} + f_{u,i + 1}
\]

但是\(i\)的值域是\([1,D]\),根本做不了,怎么办?

拉格朗日插值登场。

假设\(u\)是一个叶子结点,那么\(f_{u,i} = D - i + 1\)是一个关于\(i\)的一次多项式

由于转移方程是简单的乘法和加法的形式,可以看出来\(f_{u,i}\)就是一个关于\(i\)的多项式,到这里我们需要考虑的就是这个多项式的次数是多少。

设\(g_u\)表示\(f_{u,i}\)的次数,那么根据上面的状态转移方程,可以得到

\[f_{u,i} - f_{u, i + 1} = \prod_v f_{v,i}
\]

根据多项式基础知识,一个多项式差分,次数减一;多个多项式相乘,子树相加,那么就有

\[g_u - 1 = \sum_v g_v \Rightarrow g_u = sz_u
\]

这里\(sz_u\)表示\(u\)子树的大小

所以答案就是一个关于\(d\)的\(n\)次多项式,求出\(n+1\)个点值后即可使用拉格朗日插值得到答案。

点我看代码 (-o⌒) ☆
#include <cstdio>
#include <vector>
#include <iostream>
#define LL long long
using namespace std;
template <typename T>
inline void read(T &x) {
x = 0; int f = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);
if(f) x = ~x + 1;
}
const int N = 3010;
const LL P = 1e9 + 7;
int n, d, m;
int f[N][N << 1];
int y[N << 1];
vector <int> G[N];
void dfs(int u) {
for(int i = 1; i <= m; ++i) f[u][i] = 1;
for(auto v : G[u]) {
dfs(v);
for(int i = m; i ; --i)
f[u][i] = 1ll * f[u][i] * f[v][i] % P;
}
for(int i = m - 1; i ; --i) f[u][i] = (f[u][i] + f[u][i + 1]) % P;
}
LL fpow(LL x, int pnt = P - 2) {
LL res = 1;
for(; pnt; pnt >>= 1, x = x * x % P) if(pnt & 1) res = res * x % P;
return res;
}
int Lagrange(int x) {
if(1 <= x && x <= m) return y[x];
LL res = 0;
for(int i = 1; i <= m; ++i) {
LL p = y[i], q = 1;
for(int j = 1; j <= m; ++j)
if(i ^ j) p = p * (x - j) % P, q = q * (i - j) % P;
res = (res + p * fpow(q)) % P;
}
return res;
}
int main() {
read(n), read(d);
for(int i = 2, u; i <= n; ++i) {
read(u);
G[u].emplace_back(i);
}
m = n + 1;
dfs(1);
for(int i = 1; i <= m; ++i) y[m - i + 1] = f[1][i];
printf("%d\n",Lagrange(d));
}

[集训队互测 2012] calc

经典题

\(\text{DP}\)还是很容易,首先由于互不相等,先转化成\(a_i\)有序,然后设\(f_{i,j}\)表示已经填了\(i\)个数,值域为\([1,j]\),转移方程就是

\[f_{i,j} = jf_{i - 1,j - 1} + f_{i, j - 1}
\]

按照上面的方法,设\(g_i\)为关于\(j\)的多项式\(f_{i,j}\)的次数,那么有

\[g_i - 1 = g_i + 1 \Rightarrow g_i = 2i
\]

然后\(f_{n,i}\)的次数就是\(2n\),求\(2n+1\)个点就能把答案插出来了

点我看代码☆ ̄(>。☆)
#include <cstdio>
#include <iostream>
#define LL long long
using namespace std;
template <typename T>
inline void read(T &x) {
x = 0; int f = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);
if(f) x = ~x + 1;
}
const int N = 510;
int k, n, m;
LL P, y[N << 1], f[N][N << 1];
LL fpow(LL x, int pnt = P - 2) {
LL res = 1;
for(; pnt; pnt >>= 1, x = x * x % P) if(pnt & 1) res = res * x % P;
return res;
}
LL Lagrange(int x) {
if(1 <= x && x <= m) return y[x];
LL res = 0;
for(int i = 1; i <= m; ++i) {
LL p = y[i], q = 1;
for(int j = 1; j <= m; ++j)
if(j != i) p = p * (k - j) % P, q = q * (i - j) % P;
if(p < 0) p += P; if(q < 0) q += P;
res = (res + p * fpow(q)) % P;
}
return res;
}
int main() {
read(k), read(n), read(P), m = (n << 1) + 1;
LL fac = 1; for(int i = 1; i <= n; ++i) fac = fac * i % P;
for(int i = 0; i <= m; ++i) f[0][i] = 1;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
f[i][j] = (f[i - 1][j - 1] * j + f[i][j - 1]) % P;
for(int i = 1; i <= m; ++i) y[i] = f[n][i];
printf("%d\n",fac * Lagrange(k) % P);
}

总结

拉格朗日插值优化\(\text{DP}\)是一种优化思路,在值域比较大,容易求点值的时候可以考虑,上面给出的例子比较简单,需要在遇到具体问题时具体考虑。

拉格朗日插值优化DP的更多相关文章

  1. BZOJ.5339.[TJOI2018]教科书般的亵渎(拉格朗日插值) & 拉格朗日插值学习笔记

    BZOJ 洛谷 题意的一点说明: \(k\)次方这个\(k\)是固定的,也就是最初需要多少张亵渎,每次不会改变: 因某个怪物死亡引发的亵渎不会计分. 不难发现当前所需的张数是空格数+1,即\(m+1\ ...

  2. jzoj5683. 【GDSOI2018模拟4.22】Prime (Min_25筛+拉格朗日插值+主席树)

    题面 \(n\leq 10^{12},k\leq 100\) 题解 一眼就是一个\(Min\_25\)筛+拉格朗日插值优化,然而打完之后交上去发现只有\(60\)分 神\(tm\)还要用主席树优化-- ...

  3. BZOJ.4559.[JLOI2016]成绩比较(DP/容斥 拉格朗日插值)

    BZOJ 洛谷 为什么已经9点了...我写了多久... 求方案数,考虑DP... \(f[i][j]\)表示到第\(i\)门课,还有\(j\)人会被碾压的方案数. 那么\[f[i][j]=\sum_{ ...

  4. P4463 [集训队互测2012] calc 拉格朗日插值 dp 多项式分析

    LINK:calc 容易得到一个nk的dp做法 同时发现走不通了 此时可以考虑暴力生成函数. 不过化简那套不太熟 且最后需要求多项式幂级数及多项式exp等难写的东西. 这里考虑观察优化dp的做法. 不 ...

  5. 【BZOJ2655】calc DP 数学 拉格朗日插值

    题目大意 ​ 一个序列\(a_1,\ldots,a_n\)是合法的,当且仅当: ​ 长度为给定的\(n\). ​ \(a_1,\ldots,a_n\)都是\([1,m]\)中的整数. ​ \(a_1, ...

  6. BZOJ.2655.calc(DP/容斥 拉格朗日插值)

    BZOJ 洛谷 待补.刚刚政治会考完来把它补上了2333.考数学去了. DP: 首先把无序化成有序,选严格递增的数,最后乘个\(n!\). 然后容易想到令\(f_{i,j}\)表示到第\(i\)个数, ...

  7. 【BZOJ】4559: [JLoi2016]成绩比较 计数DP+排列组合+拉格朗日插值

    [题意]n位同学(其中一位是B神),m门必修课,每门必修课的分数是[1,Ui].B神碾压了k位同学(所有课分数<=B神),且第x门课有rx-1位同学的分数高于B神,求满足条件的分数情况数.当有一 ...

  8. BZOJ4599[JLoi2016&LNoi2016]成绩比较(dp+拉格朗日插值)

    这个题我们首先可以dp,f[i][j]表示前i个科目恰好碾压了j个人的方案数,然后进行转移.我们先不考虑每个人的分数,先只关心和B的相对大小关系.我们设R[i]为第i科比B分数少的人数,则有f[i][ ...

  9. bzoj 4559 [JLoi2016]成绩比较 —— DP+拉格朗日插值

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4559 看了看拉格朗日插值:http://www.cnblogs.com/ECJTUACM-8 ...

随机推荐

  1. 「SDOI2016」征途 题解

    「SDOI2016」征途 先浅浅复制一个方差 显然dp,可以搞一个 \(dp[i][j]\)为前i段路程j天到达的最小方差 开始暴力转移 \(dp[i][j]=min(dp[k][j-1]+?)(j- ...

  2. 使用Django2.0.4集成钉钉第三方扫码登录

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_124 钉钉作为阿里旗下的一款免费移动通讯软件,受众群体越来越多,这里我们使用Django来集成一下钉钉的三方账号登录,首先注册钉钉 ...

  3. Redis 定长队列的探索和实践

    vivo 互联网服务器团队 - Wang Zhi 一.业务背景 从技术的角度来说,技术方案的选型都是受限于实际的业务场景,都以解决实际业务场景为目标. 在我们的实际业务场景中,需要以游戏的维度收集和上 ...

  4. KingbaseES V8R6 ksql 关闭自动提交

    背景 用过oracle或mysql的人都知道,做一个dml语句,如果发现做错了,还可以rollback,但在Kingbase ksql 中,如果执行一个dml,没有先运行begin; 的话,一执行完就 ...

  5. SQL CASE语句的使用

    SQL CASE语句的使用 CASE是一个控制流语句,其作用与IF-THEN-ELSE语句非常相似,可根据数据选择值. CASE语句遍历条件并在满足第一个条件时返回值. 因此,一旦条件成立,它将短路, ...

  6. 利用c++编写bp神经网络实现手写数字识别详解

    利用c++编写bp神经网络实现手写数字识别 写在前面 从大一入学开始,本菜菜就一直想学习一下神经网络算法,但由于时间和资源所限,一直未展开比较透彻的学习.大二下人工智能课的修习,给了我一个学习的契机. ...

  7. Python数据科学手册-机器学习: k-means聚类/高斯混合模型

    前面学习的无监督学习模型:降维 另一种无监督学习模型:聚类算法. 聚类算法直接冲数据的内在性质中学习最优的划分结果或者确定离散标签类型. 最简单最容易理解的聚类算法可能是 k-means聚类算法了. ...

  8. Python数据科学手册-Pandas:数据取值与选择

    Numpy数组取值 切片[:,1:5], 掩码操作arr[arr>0], 花哨的索引 arr[0, [1,5]],Pandas的操作类似 Series数据选择方法 Series对象与一维Nump ...

  9. 关于thinkphp5.1(tp5.1)中sum计算结果不精确、不准确的问题

    使用sprintf函数处理,虽然原理没搞懂,但是问题解决了 复现: test表中有两列,值分别是-0.33和10,数据类型是float SELECT SUM(`val`) AS tp_sum FROM ...

  10. 网络基础七层模型与TCP/IP协议

    1.网络基础 1.1 什么是网络 网络就是计算机网络是一组计算机或网络设备通过有形 的线缆或无形的媒介如无线,连接起来,按照一定的 规则,进行通信的集合. 网络通信就是指终端设备之间通过计算机网络进行 ...