拉格朗日插值优化DP
拉格朗日插值优化DP
模拟赛出现神秘插值,太难啦!!
回忆拉格朗日插值是用来做什么的
对于一个多项式,如果已知它的次数为,且已知个点值,那么可以得到
所以,如果我们知道要求的东西是一个次数比较友好的多项式且容易求出一些点值,那么就可以把答案插出来。
来看两道例题
CF995F Cowmpany Cowmpensation
题意:给你一棵树,要求给每个点分配内的权值,且儿子的权值不能超过父亲的权值,对取模,
很容易得到一个,设表示u子树内u的权值大于等于的答案,那么
但是的值域是,根本做不了,怎么办?
拉格朗日插值登场。
假设是一个叶子结点,那么是一个关于的一次多项式
由于转移方程是简单的乘法和加法的形式,可以看出来就是一个关于的多项式,到这里我们需要考虑的就是这个多项式的次数是多少。
设表示的次数,那么根据上面的状态转移方程,可以得到
根据多项式基础知识,一个多项式差分,次数减一;多个多项式相乘,子树相加,那么就有
这里表示子树的大小
所以答案就是一个关于的次多项式,求出个点值后即可使用拉格朗日插值得到答案。
点我看代码 (-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
经典题
还是很容易,首先由于互不相等,先转化成有序,然后设表示已经填了个数,值域为,转移方程就是
按照上面的方法,设为关于的多项式的次数,那么有
然后的次数就是,求个点就能把答案插出来了
点我看代码☆ ̄(>。☆)
#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); }
总结
拉格朗日插值优化是一种优化思路,在值域比较大,容易求点值的时候可以考虑,上面给出的例子比较简单,需要在遇到具体问题时具体考虑。
经典的例题还有 NOI 的机器人。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】