题目

城市中有一条长度为 \(n\) 的道路,每隔 \(1\) 的长度有一个公交车站,编号从 \(0\) 到 \(n\),学校在 \(0\) 号车站的位置。其中每个公交车站(除了 \(n\) 号车站)有两个属性 \(c_i\) 和 \(v_i\),代表从这个公交车站出发的公交车的性质。\(c_i\) 代表这个从i出发的公交车,相邻两个停靠站之间的距离。\(v_i\) 表示每坐 \(1\) 站的花费。

注意,一辆公交车出发后会向 \(n\) 号车站的方向行进。同时,一名乘客只能从起点站上车,但可以从任意停靠站下车。校庆志愿者小 \(Z\) 为了帮助校友查询有关城市交通费用的问题,想知道从 \(0\) 号车站(也就是学校)出发,到达每个公交车站的最小花费,于是他找到了你。

数据规模

对于 30% 的数据满足,\(1 \le n \le 5000\)

对于 60% 的数据满足,\(1 \le n \le 10^5\)

对于另 20% 的数据满足,\(maxc = 1\)

对于 100% 的数据满足,\(1 \le n \le 10^6,1 \le maxc \le 10,1 \le ci \le maxc,1 \le vi \le 1000\)

数据存在梯度。

分析

30% 的数据

我们试着想 \(O(n^2)\) 的 \(dp\)

有 \(f_i = \min_{1 \le j < i,c_j | (i-j)}f_j + (i-j)/c_j*v_j\)

另 20% 的数据

既然有 \(maxc = 1\) 说明所有的 \(c_j=1\)

也就是说我们从 \(0\) 点开始坐车,一遇到 \(v_i\) 更小的就可以换乘,必然更优

\(O(n)\) 扫一遍就好了

100% 的数据

考虑继续优化 \(dp\)

很明显只能上斜率优化了!

很明显用不了斜率优化!

观察 \(f_i = f_j+ (i-j)/c_j*v_j\)

若需 \(c_j|(i-j)\),那么 \(i \equiv j \pmod {c_j}\)

所以我们要维护 \(i\) 的决策集合

只要 \(c_i\) 和 \(i \bmod {c_i}\) 两样东西就可以确定了

那我们就维护 \(maxc \times maxc\) 个决策集合

选用 \(vector\) 类型就好,我选择了链式前向星

然后怎么找最优决策?

我们先从题目中找性质

两站同属一个决策集合

如过后一个站的 \(v_i\) 小于 前一个站 \(v_i\), 前一个站就没有用了

然后我们就有了 \(v_i\) 单调递增

因为同属一个集合,\(c_i=c_j\) 所以 \(v_i/c_i\) 单调递增

记 \(p_i = v_i / c_i\)

那么 \(f_i = f_j+(i-j)*p_i\)

此时考虑斜率优化

若 \(j < k\) 且 \(j\) 更优

\[\begin{aligned}
f_j+(i-j)*p_j < f_k+(i-k)*p_k \\
\frac{(f_j-j*p_j)-(f_k-k*p_k)}{p_k-p_j} < i
\end{aligned}
\]

因为 \(f_j-j*p_j\) 越来越小

所以最优决策在最后,用栈维护集合即可

\(Code\)

#include<cstdio>
#include<iostream>
using namespace std; const int N = 1e6 + 5, INF = 0x3f3f3f3f;
int n, maxc;
int f[N], c[N], v[N], t[15][15]; struct node{int to, pre;}e[N];
inline void add(int c, int y, int to)
{
static int tot = 0;
e[++tot] = node{to, t[c][y]}, t[c][y] = tot;
} inline double slope(int j, int k){return (f[j] - 1.0 * j * v[j] / c[j] - f[k] + 1.0 * k * v[k] / c[k]) / (v[k] - v[j]);} inline void read(int &x)
{
x = 0; int f = 1; char ch = getchar();
while (ch < '0' || ch > '9') f = (ch == '-' ? -1 : f), ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
x *= f;
}
int buf[20];
inline void write(int x)
{
if (x < 0) putchar('-'), x = -x;
for (; x; x /= 10) buf[++buf[0]] = x % 10;
if (!buf[0]) buf[++buf[0]] = 0;
for (; buf[0]; putchar('0' + buf[buf[0]--]));
} int main()
{
freopen("bus.in", "r", stdin);
freopen("bus.out", "w", stdout);
read(n), read(maxc);
for(register int i = 0; i < n; i++) read(c[i]), read(v[i]);
f[0] = 0, add(c[0], 0, 0);
for(register int i = 1; i <= n; i++)
{
f[i] = INF;
for(register int j = 1; j <= maxc; j++)
if (t[j][i % j])
{
int y = i % j;
while (e[t[j][y]].pre && slope(e[e[t[j][y]].pre].to, e[t[j][y]].to) < 1.0 * i / j) t[j][y] = e[t[j][y]].pre;
f[i] = min(f[i], f[e[t[j][y]].to] + (i - e[t[j][y]].to) * v[e[t[j][y]].to] / c[e[t[j][y]].to]);
}
if (i == n || f[i] == INF) continue;
int y = i % c[i];
while (t[c[i]][y] && v[e[t[c[i]][y]].to] >= v[i]) t[c[i]][y] = e[t[c[i]][y]].pre;
while (e[t[c[i]][y]].pre && slope(e[e[t[c[i]][y]].pre].to, e[t[c[i]][y]].to) < slope(e[t[c[i]][y]].to, i))
t[c[i]][y] = e[t[c[i]][y]].pre;
add(c[i], y, i);
}
for(register int i = 1; i <= n; i++)
if (f[i] == INF) putchar('-'), putchar('1'), putchar(' ');
else write(f[i]), putchar(' ');
}

最新文章

  1. c#自定义日志记录
  2. jsp项目部署
  3. JAVA 实现FTP上传下载(sun.net.ftp.FtpClient)
  4. IE6 一些兼容性问题及处理方法
  5. [SAP ABAP开发技术总结]业务对象和BAPI
  6. CodeForces485B——Valuable Resources(水题)
  7. 按按钮调用PHP function函数
  8. linux查看磁盘空间
  9. Axis2 转让Webservice 介面
  10. Qt中的串口编程之三
  11. MFC窗口程序显示命令行输出窗口的方法
  12. jq选择器汇总
  13. JS 实现MVC的写法
  14. Oracle AWRDD报告生成和性能分析
  15. Vscode生成verilog例化
  16. mysql数据类型优化
  17. [原创]标记下Kendo使用中的问题, 持续更新中ing.....
  18. spring4笔记----Spring几种常用的容器后处理器
  19. python学习初始函数
  20. SSO(singlesignon)单点登录

热门文章

  1. MapStruct与lombok加载顺序问题与annotationProcessorPaths的关系?
  2. Idea中Git的常用操作及可能存在的问题
  3. 【每日一题】【小根堆&amp;边出队边入队后续节点&amp;注意判空】23. 合并K个升序链表-211128/220213
  4. 微服务开发平台 Spring Cloud Blade 部署实践
  5. ZooKeeper 3.6.X 配置参考
  6. vue中mixins(混入)的用法
  7. 9、IDEA回退Git版本
  8. GitHub车牌检测识别项目调研
  9. Hadoop详解(10) - Hadoop HA高可用
  10. .NET周报【1月第2期 2023-01-13】