拉格朗日插值
这个东西应该在很久之前就要学的结果被鸽到了现在。
我是鸽德
拉格朗日插值
拉格朗日插值解决的是一类给定多项式的点值表示让你求另一个点的函数值的问题。
先来思考这个引子:给定
有一个显然的线性代数写法,建立一个
时间复杂度
接下来进入正题。
我们直接给出拉格朗日多项式的式子
这里只给出直观解释:考虑一个点
据此,我们只要能找到一个多项式的点值表示,我们便可以求出这个多项式上任意一点的值。
代入计算,时间复杂度
接下来是一个经典应用:求
如果要用拉格朗日插值,就要明确我们要求的东西是多项式,问题转化为如何证明
记
如果我们令
如果
引理:如果数列
证明:假设
数列的通项是一个关于 的一个 次多项式,那么 ,据此可得到 注意到两项的 最高次项系数是相等的,相减后消去,因此,
的次数是 的次数减一。 次差分后,得到 次式,即常数序列。 证毕。
本题,可以定义数列
两两做差,发现序列变成了
通项为
朴素拉格朗日插值
注意到我们可以代入的点可以自选,所以我们可以寻找一批有特殊性质的点。我们尝试用
令
注意到,分母是
预处理
如果你认为上面这道题很简单,那就快来做一下例题吧!
[省选联考 2022] 填树
绝了,没看sol脑子一片空白。
感觉在赛场上10pts遗憾离场,只会一个不知道时间复杂度多少的暴力。
对于区间题,有一个想法是枚举赋值区间,但区间交容易算重,因此我们加入一定限制条件:至少有一个节点取最小值。
不加入这个限制非常好做,每个点的范围都被固定了,随便乘一下,加入限制后只需将求出的答案减
其中,
- 从父亲部分继承的贡献,这一部分是
。 点做出的贡献,每个值能做出 的贡献。
枚举取值范围
优化做法
这里是关于降低复杂度的关键是不去枚举根节点,这意味着我们需要用某种方式快速合并子树信息。
在枚举节点
假设
可以改写 dp 式子:
其中,
合并子树信息
注意到应该先统计答案再合并子树,不然会算重。
我们对每一个区间进行 dp 转移的时候,我们发现区间的交和
我们要求的是前缀和,即
const int P = 1e9 + 7, N = 205, M = 1000;
int n, k, l[N], r[N], lsh[M], tot, L, R, x[M], Z[M], C[M];
vector<int> e[N];
ll ans1, ans2, f[N], g[N], fc[N], gc[N]; // f, g, f', g'
il void dfs(int u, int fa) {
int x = max(l[u], L), y = min(r[u], R);
if (x > y) x = 1, y = 0;
int len = y - x + 1, sum = (1ll * (x + y) * (y - x + 1) / 2) % P;
f[u] = fc[u] = len, g[u] = gc[u] = sum;
for (int v : e[u]) {
if (v == fa) continue;
dfs(v, u);
f[u] += fc[u] * fc[v] % P;
g[u] += (fc[u] * gc[v] % P + fc[v] * gc[u] % P) % P;
fc[u] = (fc[u] + fc[v] * len % P) % P;
gc[u] = (gc[u] + gc[v] * len % P + fc[v] * sum % P) % P;
}
}
il void calc(int opt) {
dfs(1, 0);
for (int i = 1; i <= n; ++i) ans1 += f[i] * opt, ans2 += g[i] * opt;
ans1 = (ans1 % P + P) % P, ans2 = (ans2 % P + P) % P;
}
il int qpow(int x, int y) {
int ret = 1;
for (; y; y >>= 1, x = 1ll * x * x % P) if (y & 1) ret = 1ll * ret * x % P;
return ret;
}
il int lagrange(int p, int x, int* X, int* Y) { // p 次多项式
int ret = 0;
for (int i = 0; i < p; ++i) {
int fz = 1, fm = 1;
for (int j = 0; j < p; ++j)
if (i != j) fz = 1ll * fz * (x - X[j]) % P, fm = 1ll * fm * (X[i] - X[j]) % P;
ret = (ret + 1ll * fz * qpow(fm, P - 2) % P * Y[i] % P) % P;
}
return ret;
}
int main() {
// freopen("tree.in", "r", stdin);
// freopen("tree.out", "w", stdout);
read(n), read(k);
for (int i = 1; i <= n; ++i) {
read(l[i]), read(r[i]);
lsh[++tot] = l[i], lsh[++tot] = max(0, l[i] - k);
lsh[++tot] = r[i], lsh[++tot] = max(0, r[i] - k);
lsh[0] = max(lsh[0], r[i] + 1);
}
sort(lsh, lsh + tot + 1); tot = unique(lsh, lsh + tot + 1) - lsh;
for (int i = 1; i < n; ++i) {
int u = read(), v = read();
// assert(u <= n && v <= n);
e[u].eb(v), e[v].eb(u);
}
for (int i = 0, j; i < tot; ++i) {
L = lsh[i], R = lsh[i] + k;
for (j = 0; j < n + 2; ++j, ++R) {
if (lsh[i] + j == lsh[i + 1]) break;
calc(1); ++L; calc(-1);
x[j] = lsh[i] + j, Z[j] = ans1, C[j] = ans2;
}
if (lsh[i] + j < lsh[i + 1]){
ans1 = lagrange(j, lsh[i + 1] - 1, x, Z);
ans2 = lagrange(j, lsh[i + 1] - 1, x, C);
}
}
write(ans1), write(ans2);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探