首页 > 其他分享 >拉格朗日插值优化DP

拉格朗日插值优化DP

时间:2022-08-14 19:56:40浏览次数:53  
标签:拉格朗 ch 插值 多项式 int res DP getchar

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

标签:拉格朗,ch,插值,多项式,int,res,DP,getchar
From: https://www.cnblogs.com/DCH233/p/16586148.html

相关文章

  • P7154 [USACO20DEC] Sleeping Cows P(DP)
    主要是状态设计比较难想,但其实可以理性地推出来。P7154[USACO20DEC]SleepingCowsP考虑最终一个合法状态是怎么样的:一定是一堆小牛棚,一堆大奶牛,最大的牛棚小于最小的......
  • P6144 [USACO20FEB]Help Yourself P(DP+线段树)
    P6144[USACO20FEB]HelpYourselfP将线段按照了\(r\)排序,设右端点为\(r\)的答案为\(f_r\),发现这样转移非常困难。\(\color{yellow}{\bigstar\texttt{Trick}}\):区间......
  • CF559E Gerald and Path(DP)
    CF559EGeraldandPath设\(dp(i,p)\)表示完成前\(i\)条线段的覆盖,最右端位于\(p\)点的最大收益。转移?向下一条线段转移时加上他们中间的距离?发现这样没有办法统计......
  • CF856D Masha and Cactus(树上 DP+抵消贡献技巧)
    CF856DMashaandCactus我们先捞出一个根节点,那么一次旋变就是对路径上点的覆盖。设\(dp_{i,0}\)表示\(i\)没有选择时子树内最大收益,\(dp_{i,1}\)表示\(i\)选择......
  • CF512D Fox And Travelling(DP 计数)
    CF512DFoxAndTravelling给定一张\(n\)个点\(m\)条边的无向图,每次选择一个叶子结点并将它和连接它的边删除。对于每个\(k\in[0,n]\),问有序选择\(k\)个点的方案......
  • 【杂题乱写】AtCoder dp 26题
    AtCoderdp26题原比赛链接洛谷题单链接A-Frog1题目已然给出了转移方程,设\(dp_i\)为到第\(i\)块石头的最小代价。转移方程:\[dp_i=\min(dp_{i-1}+|h_i-h_{i-1}......
  • 【$dp$】$\text{LuoguP6570}$ 优秀子序列
    \(\text{LuoguP6570}\)优秀子序列读完题大概能yy到一个转移,即枚举两个不相交的子集然后转移。其实这题的顺序都无所谓,应该排个序,或者直接在值域上操作。\(DP\),用\(f......
  • 数位Dp
    代码拍卖会题意问有[L-R]有多少个数满足每一位都至少有1,从左到右不减同时要能被P整除,位数<=\(1e18\).p<=500)思路位数贼大,基本上别想着枚举有关位数的东西单调......
  • dp 学习笔记
    一.前言动态规划是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。其思想灵活多变,在OI中占有重要地位,必须掌握熟练。二背包问题背包问题都类......