【斜率优化】【P5468】 [NOI2019]回家路线
Description
给定 \(n\) 点,这 \(n\) 个点由 \(m\) 班列车穿插连结。对于第 \(i\) 班列车,会在 \(p_i\) 时刻从 \(x_i\) 站点出发开向 \(y_i\) 站点,到站时间为 \(q_i\)。现在从 \(1\) 号节点出发,经过多次换乘到达节点 \(n\)。一次换乘是指对于两班列车,假设分别为 \(u\) 号与 \(v\) 号列车,若 \(y_u = x_v\) 并且 \(q_u \leq p_v\),那么小猫可以乘坐完 \(u\) 号列车后在 \(y_u\) 号站点等待 \(p_v - q_u\) 个时刻,并在时刻 \(p_v\) 乘坐 \(v\) 号列车。要求最小化路程中的烦躁值。烦躁值的计算方法是:给定参数 \(A,B,C\),对于每次等待,假设等待了 \(t\) 个时刻,那么烦躁值增加 \(A t^2 + B t + C\)。另外如果在时刻 \(T\) 到达节点 \(n\),则狂躁值再增加 \(T\)。
Limitations
\(1 \leq n,~m \leq 2 \times 10^5\)
\(0 \leq A \leq 10,~~0 \leq B,~C \leq 10^6\)
\(1 \leq p_i \leq q_i \leq 10^3\)
保证 \(1\) 可以到达 \(n\)。
Solution
好像正解是以时间为状态DP来着……然而拿到这个题的第一反应是DP每条边到 \(n\) 的贡献。
考虑设 \(f_i\) 是第 \(i\) 条边开始出发,到达 \(n\) 号节点对烦躁值的最小贡献。于是有转移方程:
\]
即
\]
将与 \(j\) 无关的项提出大括号,得到
\]
整理得
\]
设 \(g_i = A q_i^2 - Bq_i - p_i + C\),\(h_i = Ap_i^2 + (B+1)p_j\)
上式即为
\]
移相得到
\]
注意到 \(g_i\) 是一个与 \(i\) 有关的常数,那么最小化 \(f_i\) 只需要最小化 \(f_i + g_i\)
如果将 \(f_j + h_j\) 看作纵坐标, \(q_j\) 看作横坐标,上述方程可以看成一条斜率为常数 \(2Aq_i\) 的直线,在所有满足条件的 \(j\) 中选择一个点,使得直线过这个点,最小化直线在 \(y\) 轴上的截距 \(f_i + g_i\)。
那么所有满足条件的点显然在一个下突壳上,证明上可以考虑如果一个点不在下突壳上那么能找到一个更优的 \(j\)。
然后考虑如果 \(i\) 会从 \(j\) 转移过来,那么一定有 \(q_i \leq p_j\),又因为 \(p_j < q_j\),因此 \(q_i < q_j\),于是按照 \(q\) 的不升序进行排序即可。
在转移时,只需要在每个点维护一个凸壳表示所有可能被选择的点,然后再维护一个 set
记录该点上已经被计算但是没有插入凸壳的点。set
内部按照 \(p\) 的不升序排序,每次要转移一条边 \(i\) 的时候先将终点中 \(p\) 不小于 \(q_i\) 的边插入凸壳,由于转移是按照 \(q_i\) 的顺序进行的,已经被插入凸壳的点一定是合法可以转移的。
另外注意到 \(q_i\) 是单调不升的,于是斜率 \(2Aq_i\) 也是单调升的,再考虑到插入的横轴 \(p_j\) 也是单调不升的,因此可以使用单调队列维护每个点的凸壳即可。
需要注意的一点细节是凸壳的横轴是从大到小插入的,在维护的时候不要把大于号小于号写反。
一共进行了 \(m\) 次转移,每次转移复杂度 \(O(1)\),因此 DP 过程的时间复杂度是 \(O(m)\),但是由于进行了排序,且每条边插入在 set
中 1 次,所以整个算法的时间复杂度 \(O(m \log m)\)。
Code
#include <cstdio>
#include <set>
#include <queue>
#include <algorithm>
const int maxn = 200005;
const ll INF = 1ll << 50;
int n, m;
ll A, B, C, ans;
ll frog[maxn], g[maxn], h[maxn];
struct M {
int x, y, p, q;
inline bool operator<(const M &_others) const {
return this->q > _others.q;
}
};
M MU[maxn];
struct Cmp {
inline bool operator() (const int &_a, const int &_b) {
if (MU[_a].p != MU[_b].p) {
return MU[_a].p > MU[_b].p;
} else {
return _a < _b;
}
}
};
std::deque<int>Q[maxn];
std::set<int, Cmp>s[maxn];
int query(const int x);
void free(const int x, const int y);
void insert(const int x, const int y);
signed main() {
freopen("route.in", "r", stdin);
freopen("route.out", "w", stdout);
qr(n); qr(m); qr(A); qr(B); qr(C);
for (int i = 1; i <= m; ++i) {
qr(MU[i].x); qr(MU[i].y); qr(MU[i].p); qr(MU[i].q);
}
frog[m + 1] = 1ll << 50;
std::sort(MU + 1, MU + 1 + m);
for (int i = 1; i <= m; ++i) {
g[i] = A * MU[i].q * MU[i].q - B * MU[i].q - MU[i].p + C;
h[i] = A * MU[i].p * MU[i].p + (B + 1) * MU[i].p;
}
for (int i = 1; i <= m; ++i) if (MU[i].y == n) {
frog[i] = MU[i].q - MU[i].p;
s[MU[i].x].insert(i);
} else {
int y = MU[i].y;
free(y, MU[i].q);
int j = query(i);
if (!j) {
frog[i] = INF;
continue;
}
frog[i] = frog[j] + g[i] + h[j] - ((A * MU[i].q * MU[j].p) << 1);
s[MU[i].x].insert(i);
}
ans = 1ll << 50;
for (int i = 1; i <= m; ++i) if (MU[i].x == 1) {
ans = std::min(frog[i] + A * MU[i].p * MU[i].p + B * MU[i].p + C + MU[i].p, ans);
}
qw(ans, '\n', true);
return 0;
}
void free(const int x, const int y) {
while (!s[x].empty()) {
auto u = *s[x].begin();
if (MU[u].p >= y) {
insert(x, u);
s[x].erase(u);
} else {
break;
}
}
}
inline std::pair<ll, ll> calc(const int x) {
return std::make_pair(frog[x] + h[x], 1ll * MU[x].p);
}
inline bool judge(const int x, const int y, const int z) {
auto i = calc(x), j = calc(y), k = calc(z);
return (k.first <= j.first) && ((i.first - k.first) * (i.second - j.second) < (i.first - j.first) * (i.second - k.second));
}
void insert(const int x, const int y) {
while (Q[x].size() > 1) {
int i = Q[x].back(); Q[x].pop_back(); int j = Q[x].back();
if (judge(j, i, y)) {
Q[x].push_back(i); break;
}
}
Q[x].push_back(y);
}
int query(const int p) {
int x = MU[p].y;
while (Q[x].size() > 1) {
int i = Q[x].front(); Q[x].pop_front(); int j = Q[x].front();
auto s = calc(i), t = calc(j); ll k = ((A * MU[p].q) << 1);
if ((k * (s.second - t.second)) > (s.first - t.first)) {
Q[x].push_front(i);
break;
}
}
return Q[x].size () ? Q[x].front() : 0;
}
【斜率优化】【P5468】 [NOI2019]回家路线的更多相关文章
- P5468 [NOI2019]回家路线 斜率优化 dp
LINK:回家路线 (文化课 oi 双爆炸 对 没学上的就是我.(我错了不该这么丧的. 不过还能苟住一段时间.当然是去打NOI了 这道题去年同步赛的时候做过.不过这里再次提醒自己要认真仔细的看题目 不 ...
- Luogu P5468 [NOI2019]回家路线 (斜率优化、DP)
题目链接: (luogu) https://www.luogu.org/problemnew/show/P5468 题解: 爆long long毁一生 我太菜了,这题这么简单考场上居然没想到正解-- ...
- 【题解】Luogu P5468 [NOI2019]回家路线
原题传送门 前置芝士:斜率优化 不会的可以去杜神博客学 这道题我考场上只会拆点跑最短路的70pts做法 后来回家后发现错误的爆搜都能拿满分(刀片) 还有很多人\(O(mt)\)过的,还是要坚持写正解好 ...
- P5468 [NOI2019]回家路线
传送门 看题目一眼斜率优化,然后写半天调不出来 结果错误的 $dfs$ 有 $95$ 分?暴力 $SPFA$ 就 $AC$ 了? 讲讲正解: 显然是斜率优化的式子: 先不考虑 $q_{s_k}$ 的贡 ...
- [NOI2019]回家路线
[NOI2019]回家路线 题目大意: 有\(n\)个站点,\(m\)趟车,每趟车在\(p_i\)时从\(x_i\)出发,\(q_i\)时到达\(y_i\). 若小猫共乘坐了\(k\)班列车,依次乘坐 ...
- [NOI2019]回家路线(最短路,斜率优化)
终于把这鬼玩意弄完了-- 为什么写的这么丑-- (顺便吐槽 routesea) 最短路的状态很显然:\(f[i]\) 表示从第 \(i\) 条线下来的最小代价. 首先明显要把那个式子拆开.直觉告诉我们 ...
- NOI2019 回家路线 DP
「NOI2019」回家路线 链接 loj 思路 f[i][j]第i个点,时间为j,暴力转移 复杂度O(m*t),好像正解是斜率优化,出题人太不小心了233 代码 #include <bits/s ...
- luogu 5468 [NOI2019]回家路线 最短路/暴力
想写一个 70 pts 算法,结果数据水,直接就切了 最短路: // luogu-judger-enable-o2 #include<bits/stdc++.h> using namesp ...
- LOJ 3156: 「NOI2019」回家路线
题目传送门:LOJ #3156. 题意简述: 有一张 \(n\) 个点 \(m\) 条边的有向图,边有两个权值 \(p_i\) 和 \(q_i\)(\(p_i<q_i\))表示若 \(p_i\) ...
随机推荐
- Appium+python自动化(五)- 模拟器(超详解)
简介 Appium是做安卓自动化的一个比较流行的工具,对于想要学习该工具但是又局限于或许当前有些小伙伴没 android 手机来说,可以通过安卓模拟器来解决该问题,下面就讲解使用appium连接安卓模 ...
- C# vb .NET识别读取QR二维码
二维码比条形码具有更多优势,有些场合使用二维码比较多,比如支付.那么如何在C#,.Net平台代码里读取二维码呢?答案是使用SharpBarcode! SharpBarcode是C#快速高效.准确的条形 ...
- C# winform 启动外部程序
//class里面放入这段代码[DllImport("shell32.dll")]public static extern int ShellExecute(IntPtr hwnd ...
- Delphi - 采用第三方控件TMS、SPComm开发串口调试助手
第三方控件TMS.SPComm的下载与安装 盒子上可搜索关键字进行下载,TMS是.dpk文件,SPComm.pas文件: 安装方法自行百度,不做赘述. 通过TMS控件进行界面布局 界面预览: Delp ...
- 使用Hybris的customer coupon进行促销活动(promotion)
登录Backoffice,在Coupon菜单里创建一个新的类型为Customer Coupon的优惠券: 在菜单Marketing->Promotion Rules里,创建一条新的促销规则Pro ...
- 静态资源上传至远程ftp服务器,ftp工具类封装
工具类,是一个单独的工程项目 提取必要信息至ftp.properties配置文件中 ftp_host=192.168.110.128 ftp_port=21 ftp_username=ftpuser ...
- C 语言实现回调函数
优点 不需要改变调用的主函数,只需添加命令和相应函数. #include "stdio.h" #include "stdlib.h" #include &quo ...
- [转]【HttpServlet】HttpServletRequest接口
建立时间:2019.6.28 &6.29 1.HttpServletRequest概述 我们在创建Servlet时会覆盖service()方法,或doGet()/doPost(),这些方法都有 ...
- HDU6583:Typewriter(dp+后缀自动机)
传送门 题意: 给出\(p,q\),现在要你生成一个字符串\(s\). 你可以进行两种操作:一种是花费\(p\)的代价随意在后面添加一个字符,另一种是花费\(q\)的代价可以随意赋值前面的一个子串. ...
- angularjs中设置select的选中项
最近用angularjs比较多,里面有很多自己的方法,都不咋会用,这篇只是个笔记,防止自己忘记 <select style="width:100%" ng-model=&qu ...