专栏文章
CF 280E Sequence Transformation
CF280E题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miowkcsm
- 此快照首次捕获于
- 2025/12/03 02:19 3 个月前
- 此快照最后确认于
- 2025/12/03 02:19 3 个月前
只会复读其他题解了/ll。
首先有一个 dp 是记 表示 时的最小代价,转移形如 。
不过此时遇到的问题是 的上界不仅很大而且可能为小数,是十分不好维护的。
此时一个很妙的想法是考虑把 当作是一个关于 的函数,即看作 。
这个函数可能会有一些分段的情况,但是首先对这个取 的操作感受一下,段数其实并不多。
这个函数可能会有一些分段的情况,但是首先对这个取 的操作感受一下,段数其实并不多。
于是考虑分段维护 ,这样的好处将会在后文体现。
首先来考虑初值:,那么有 。
然后来考虑第一次转移:。
先来考虑简化这个 , 是具有性质的,这是单调递增的,所以考虑找到使 取到最小值的 ,那么 。
令 ,那么会发现 ,即 也是单调不降的。
令 ,那么会发现 ,即 也是单调不降的。
那么 ,所以 ,会发现这依然是不降的。
于是可以发现,这之后的每一步转移也是类似的,都可以说明 其实是单调不降的。
那么每次转移都可以考虑求出使得取到 的最小值的值 ,这是容易通过 来发现的。
于是只需要把 从 处劈开,左边平移 位,右边平移 位,并把 都填充上 ,最后再加上 这个函数,就实现了 的转移。
于是只需要把 从 处劈开,左边平移 位,右边平移 位,并把 都填充上 ,最后再加上 这个函数,就实现了 的转移。
需要注意的是 并不一定是连续的,通常 会满足 ,但是在定义域限制下可能并不能找到合适的 ,但此时 一定会在端点 / 分界点处(满足左侧 ,右侧 ,不过端点处可能还需要一些特判)。
于是每一步转移都可以 完成,总的复杂度就为 。
对于实现,可以考虑维护分界点的点值 ,这样更容易处理平移的问题,因为 是一次函数,所以分界点之间的 是容易表示出来的。
由于这种做法的空间并不是十分友好,点值需要滚掉,可以考虑记下每一个点值是 种情况的哪一种得到的,在构造时倒推,具体可以见实现。
还有就是,建议用
Clong double。#include <bits/stdc++.h>
#define double long double
constexpr double eps = 1e-9;
constexpr int maxn = 6000 + 10;
int n, m, x[maxn], a, b;
double p[maxn * 2], f[maxn * 2];
double pu[maxn];
char lst[maxn][maxn * 2];
double y[maxn];
int main() {
scanf("%d%d%d%d", &n, &m, &a, &b);
for (int i = 1; i <= n; i++) scanf("%d", &x[i]);
p[1] = 1.0, f[1] = 2.0 * (p[1] - x[1]);
p[2] = m, f[2] = 2.0 * (p[2] - x[1]);
for (int i = 2; i <= n; i++) {
for (int j = 0; j <= (i - 1) * 2; j++) {
if ((j == 0 || f[j] < eps) && (j == (i - 1) * 2 || -eps < f[j + 1])) {
if (p[j + 1] - p[j] > eps && 1 <= j && j < (i - 1) * 2) {
pu[i - 1] = p[j] + (-f[j]) * (p[j + 1] - p[j]) / (f[j + 1] - f[j]);
} else if (j >= 1) {
pu[i - 1] = p[j];
} else {
pu[i - 1] = p[1];
}
for (int k = (i - 1) * 2; k >= j + 1; k--) {
p[k + 2] = p[k] + b, f[k + 2] = f[k], lst[i][k + 2] = 2;
}
p[j + 1] = pu[i - 1] + a, f[j + 1] = 0, lst[i][j + 1] = 1;
p[j + 2] = pu[i - 1] + b, f[j + 2] = 0, lst[i][j + 2] = 1;
for (int k = 1; k <= j; k++) {
p[k] += a, lst[i][k] = 0;
}
break;
}
}
for (int j = 1; j <= i * 2; j++) {
f[j] += 2.0 * (p[j] - x[i]);
}
}
const int low = 1 + a * (n - 1), upp = m;
double val = -1;
for (int i = 0; i <= n * 2; i++) {
if ((i == 0 || f[i] < eps) && (i == n * 2 || -eps < f[i + 1])) {
double u;
if (p[i + 1] - p[i] > eps && 1 <= i && i < 2 * n) {
u = p[i] + (-f[i]) * (p[i + 1] - p[i]) / (f[i + 1] - f[i]);
} else if (i >= 1) {
u = p[i];
} else {
u = p[1];
}
if (low - eps < u && u < upp + eps) {
val = u;
} else if (u < low + eps) {
val = low;
} else {
val = upp;
}
}
}
y[n] = val;
for (int i = n; i > 1; ) {
for (int j = 1; j < i * 2; j++) {
if (p[j] - eps < val && val < p[j + 1] + eps) {
if (lst[i][j] == 1 && lst[i][j + 1] == 1) {
val = pu[i - 1];
} else if (lst[i][j] == 0) {
val -= a;
} else {
val -= b;
}
}
}
for (int j = 1; j <= i * 2; j++) {
if (lst[i][j] == 0) {
p[j] -= a;
}
if (lst[i][j] == 2) {
p[j - 2] = p[j] - b;
}
}
y[--i] = val;
}
double ans = 0;
for (int i = 1; i <= n; i++) {
printf("%.10Lf ", y[i]);
ans += (y[i] - x[i]) * (y[i] - x[i]);
}
printf("\n%.10Lf\n", ans);
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...