博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
LOJ 3156: 「NOI2019」回家路线
阅读量:5129 次
发布时间:2019-06-13

本文共 3241 字,大约阅读时间需要 10 分钟。

题目传送门:。

题意简述:

有一张 \(n\) 个点 \(m\) 条边的有向图,边有两个权值 \(p_i\)\(q_i\)\(p_i<q_i\))表示若 \(p_i\) 时刻在这条边的起点,则 \(q_i\) 时刻能到达这条边的终点。

你需要规划一条路线,使得从起点 \(1\) 号点出发,沿着这条路线到达终点 \(n\) 号点。

假设路线依次经过的边为 \(\{a_1,a_2,\ldots,a_k\}\),则需要保证 \(q_{a_{i-1}}\le p_{a_i}\)

而这条路线的代价是 \(\displaystyle q_{a_k}+\sum_{i=2}^{k}f(p_{a_i}-q_{a_{i-1}})+f(p_{a_1})\),其中 \(f(x)=\mathrm{A}x^2+\mathrm{B}x+\mathrm{C}\)

你需要使得你规划的路径的代价最小,输出这个最小代价。

题解:

转移之间的代价是一个二次函数,这是显然的斜率优化 DP 模型。

考虑 \(\mathrm{f}[i]\) 表示经过第 \(i\) 条边后的 \(\sum f(p_{a_j}-q_{a_{j-1}})\),化简式子:

\[\begin{aligned}\mathrm{f}[i]&=\min_{y_j=x_i,q_j\le p_i}\{\mathrm{f}[j]+\mathrm{A}(p_i-q_j)^2+\mathrm{B}(p_i-q_j)+\mathrm{C}\}\\&=\min_{y_j=x_i,q_j\le p_i}\{\mathrm{f}[j]+\mathrm{A}p_i^2-2\mathrm{A}p_iq_j+\mathrm{A}q_j^2+\mathrm{B}p_i-\mathrm{B}q_j+\mathrm{C}\}\\&=\min_{y_j=x_i,q_j\le p_i}\{\mathrm{f}[j]+\mathrm{A}q_j^2-\mathrm{B}q_j-2\mathrm{A}p_iq_j\}+\mathrm{A}p_i^2+\mathrm{B}p_i+\mathrm{C}\end{aligned}\]

其中斜率的表达式是显然的,\(x\) 坐标是 \(q_i\)\(y\) 坐标是 \(\mathrm{f}[i]+\mathrm{A}q_i^2-\mathrm{B}q_i\),令 \(\mathrm{D}_i=\mathrm{A}p_i^2+\mathrm{B}p_i+\mathrm{C}\)

\(\displaystyle\mathrm{f}[i]=\mathrm{D}_i+\min_{y_j=x_i,q_j\le p_i}\{y_j-2\mathrm{A}p_ix_j\}\)

考察两个合法转移点 \(j\)\(k\)\(x_j<x_k\)),转移点 \(j\) 比转移点 \(k\) 优当且仅当:

\[\begin{aligned}y_j-2\mathrm{A}p_ix_j&<y_k-2\mathrm{A}p_ix_k\\2\mathrm{A}p_i(x_k-x_j)&<y_k-y_j\\\frac{y_k-y_j}{x_k-x_j}&>2\mathrm{A}p_i\end{aligned}\]

因为不等号是大于号,所以维护下凸壳。

注意,为了方便,更新顺序为按照 \(p_i\) 从小到大,这样右侧斜率是递增的,但是并不能更新完立刻加入凸壳,而是应该等到轮到相应的 \(p_i\) 时再加入。

以下是代码,时间复杂度为 \(\mathcal{O}(n+m+\max q)\)

#include 
#include
#include
typedef double db;const int MN = 100005, MM = 200005, MQ = 1005;int N, M, _A, _B, _C, Ans = 0x3f3f3f3f;int d[MN], eu[MM], ev[MM], ep[MM], eq[MM];std::vector
vp[MQ], vq[MQ];int X[MM], Y[MM], f[MM];int _stk[MM], *stk[MN], _l[MN], _r[MN];inline db Slope(int i, int j) { if (X[i] == X[j]) return Y[i] == Y[j] ? 0 : Y[i] < Y[j] ? 1e99 : -1e99; return (db)(Y[j] - Y[i]) / (X[j] - X[i]);}int main() { freopen("route.in", "r", stdin); freopen("route.out", "w", stdout); scanf("%d%d%d%d%d", &N, &M, &_A, &_B, &_C); ev[0] = 1, vq[0].push_back(0), ++d[1]; for (int i = 1; i <= M; ++i) { scanf("%d%d%d%d", &eu[i], &ev[i], &ep[i], &eq[i]); vp[ep[i]].push_back(i); vq[eq[i]].push_back(i); ++d[ev[i]]; } stk[0] = _stk; for (int i = 1; i <= N; ++i) stk[i] = stk[i - 1] + d[i - 1], _l[i] = 1; for (int t = 0; t <= 1000; ++t) { for (auto i : vq[t]) { int u = ev[i], *st = stk[u], l = _l[u], &r = _r[u]; while (l < r && Slope(st[r - 1], st[r]) > Slope(st[r], i)) --r; st[++r] = i; if (u == N && Ans > f[i] + eq[i]) Ans = f[i] + eq[i]; } for (auto i : vp[t]) { int u = eu[i], *st = stk[u], &l = _l[u], r = _r[u]; while (l < r && Slope(st[l], st[l + 1]) < 2 * _A * t) ++l; if (l <= r) f[i] = Y[st[l]] - 2 * _A * t * X[st[l]] + _A * t * t + _B * t + _C; else f[i] = 0x3f3f3f3f; X[i] = eq[i], Y[i] = f[i] + _A * eq[i] * eq[i] - _B * eq[i]; } } printf("%d\n", Ans); return 0;}

转载于:https://www.cnblogs.com/PinkRabbit/p/NOI2019D1T1.html

你可能感兴趣的文章
网站搭建(一)
查看>>
SDWebImage源码解读之SDWebImageDownloaderOperation
查看>>
elastaticsearch
查看>>
postgreSQL 简单命令操作
查看>>
Spring JDBCTemplate
查看>>
Radon变换——MATLAB
查看>>
第五章笔记
查看>>
Iroha and a Grid AtCoder - 1974(思维水题)
查看>>
gzip
查看>>
转负二进制(个人模版)
查看>>
LintCode-Backpack
查看>>
查询数据库锁
查看>>
[LeetCode] Palindrome Number
查看>>
我对于脚本程序的理解——百度轻应用有感
查看>>
SQL更新某列包含XX的所有值
查看>>
网易味央第二座猪场落户江西 面积超过3300亩
查看>>
面试时被问到的问题
查看>>
spring 事务管理
查看>>
VS2008 去掉msvcr90的依赖
查看>>
当前记录已被另一个用户锁定
查看>>