@description@

机场中常常见到滑行道:假如一个滑行道的运行速度为 s,你的行走速度为 v,则你的真实速度为 s + v。

Limak 在数轴上走,想要从 0 走到 L。

数轴上有 n 个不相交的滑行道(但是可以端点重叠),第 i 个滑行道占用区间 [xi, yi],且它的运行速度为 si。

Limak 想要恰当地分配他的能量:他的初始能量为 0,且在任意时刻必须为非负数。

每个时刻 Limak 都可以随意调整他的速度 v,但必须在 [0, 2] 中。

每单位时间内他将失去 v 的能量,但同时会恢复 1 的能量。即他将获得 (1-v) 的能量。

求到达 L 的最短时间。

Input

第一行两个整数 n, L (1≤n≤200000, 1≤L≤10^9),含义如上。

接下来 n 行,每行包含整数 xi, yi 以及实数 si (0≤xi<yi≤L, 0.1≤si≤10.0)。

保证 y[i] <= x[i+1],对于 1 <= i < n。

Output

输出一个实数,表示最短时间。

Examples

input

1 5

0 2 2.0

output

3.000000000000

input

1 5

2 4 0.91

output

3.808900523560

@solution@

一个敏锐的选手应该感觉得到,这个能量 e 的定义与位移 x 大同小异:

能量 e 定义每单位时间增加 (1-v),位移 x 定义为每单位时间增加 v,则 e + x 每单位时间增加 1。

于是得到某段时间内 e + x = t,所以 v 这个量就没什么价值了。但是要保证 0 <= x <= 2*t(对应速度 v 的取值区间在 [0, 2] 中这一限制)。

将没有滑行道的地方看作运行速度为 0 的滑行道。

考虑某一个滑行道,其长度为 l,运行速度为 s。则令完全地通过这个滑行道耗费时间为 t,有 l = s*t + x = s*t + (t - e) = (s + 1)*t - e。

这样一来,对于某一个滑行道,实际上只有两个量是不确定的:时间 t 与对应时间内的能量变化量 e。

我们对于每个滑行道,建立以 e 为自变量,t 为因变量的函数关系,即:

\[t = \frac{l + e}{s + 1}
\]

尝试取出这个函数的定义域。根据 0 <= x <= 2*t 得到 0 <= t - e <= 2*t,再代入 t = (l + e) / (s + 1) 就可以得到 e 的范围(过程省略):

\[-\frac{l}{s + 2}\le e \le \frac{l}{s}
\]

需要注意 s 可以取 0,这时候 e 的上界为正无穷。

我们的问题转变为,求一个数列 e[1], e[2], ...,使得 ei 在一个给定的范围内,且这个数列的任意前缀和为非负整数(这个数列表示每一个滑行道的能量变化值),而 f1(e[1]) + f2(e[2]) + ... 最大。

观察上面我们的 e-t 函数,它是一次函数且单增,即 e 增大则 t 增大。

注意到这个问题其实很像括号匹配问题。假如 e[i] 减小,则必须要有一个 e[j](j < i)要增大,且变化量相同。

如果 fi(e[i]) 的斜率 ≥ fj(e[j]) 的斜率,则这个方法是一定优秀的;否则是一定不优秀的。

于是我们就可以设计一个贪心算法:将所有函数按斜率从大到小排序,对于当前函数 fi,从 i 之前取走尽量多的能量贡献给 i。容易验证这个算法的正确性。

具体实现,可以先将 e[i] 设置为其上界,维护其前缀和。然后如果扫描到某个 e[x],尝试将 e[x] 取到最小值,同时保证前缀和非负。

这个线段树随便搞搞。

因为 s = 0 时,e 无上界。代码实现时有些小 trick。

总时间复杂度为 O(nlogn)。

@accepted code@

  1. #include<cstdio>
  2. #include<algorithm>
  3. using namespace std;
  4. typedef double db;
  5. const int MAXN = 200000;
  6. const db EPS = 1E-7;
  7. const db INF = 1E10;
  8. struct segtree{
  9. #define lch (x<<1)
  10. #define rch (x<<1|1)
  11. struct node{
  12. int l, r;
  13. db tg, mn;
  14. }t[8*MAXN + 5];
  15. void build(int x, int l, int r) {
  16. t[x].l = l, t[x].r = r, t[x].mn = 0;
  17. if( l == r ) return ;
  18. int mid = (l + r) >> 1;
  19. build(lch, l, mid), build(rch, mid + 1, r);
  20. }
  21. void pushup(int x) {
  22. t[x].mn = min(t[lch].mn, t[rch].mn);
  23. }
  24. void pushdown(int x) {
  25. if( t[x].tg ) {
  26. t[lch].tg += t[x].tg, t[rch].tg += t[x].tg;
  27. t[lch].mn += t[x].tg, t[rch].mn += t[x].tg;
  28. t[x].tg = 0;
  29. }
  30. }
  31. void modify(int x, int l, int r, db k) {
  32. if( l > t[x].r || r < t[x].l )
  33. return ;
  34. if( l <= t[x].l && t[x].r <= r ) {
  35. t[x].tg += k, t[x].mn += k;
  36. return ;
  37. }
  38. pushdown(x);
  39. modify(lch, l, r, k);
  40. modify(rch, l, r, k);
  41. pushup(x);
  42. }
  43. db query(int x, int l, int r) {
  44. if( l > t[x].r || r < t[x].l )
  45. return INF;
  46. if( l <= t[x].l && t[x].r <= r )
  47. return t[x].mn;
  48. pushdown(x);
  49. return min(query(lch, l, r), query(rch, l, r));
  50. }
  51. }T;
  52. struct node{
  53. db v0, s; int id;
  54. friend bool operator < (node a, node b) {
  55. return a.v0 < b.v0;
  56. }
  57. }a[2*MAXN + 5];
  58. db e[2*MAXN + 5];
  59. int n, L;
  60. int main() {
  61. scanf("%d%d", &n, &L);
  62. int lst = 0, cnt = 0;
  63. for(int i=1;i<=n;i++) {
  64. int x, y; db s;
  65. scanf("%d%d%lf", &x, &y, &s);
  66. cnt++, a[cnt].id = cnt, a[cnt].v0 = 0, a[cnt].s = x - lst;
  67. lst = y;
  68. cnt++, a[cnt].id = cnt, a[cnt].v0 = s, a[cnt].s = y - x;
  69. }
  70. cnt++, a[cnt].id = cnt, a[cnt].v0 = 0, a[cnt].s = L - lst;
  71. sort(a + 1, a + cnt + 1); T.build(1, 1, cnt);
  72. for(int i=1;i<=cnt;i++)
  73. if( a[i].v0 ) T.modify(1, a[i].id, cnt, e[i] = a[i].s/a[i].v0);
  74. else e[i] = 0;
  75. for(int i=1;i<=cnt;i++) {
  76. // printf("%lf %lf %lf\n", e[i], a[i].s, a[i].v0);
  77. }
  78. for(int i=1;i<=cnt;i++) {
  79. db x = e[i] - (-a[i].s/(a[i].v0 + 2)), y = T.query(1, a[i].id, cnt), z = min(x, y);
  80. // printf("%d %lf %lf\n", i, x, y);
  81. T.modify(1, a[i].id, cnt, -z), e[i] -= z;
  82. }
  83. db ans = 0;
  84. for(int i=1;i<=cnt;i++) {
  85. ans += (e[i] + a[i].s) / (a[i].v0 + 1);
  86. // printf("%d : %lf %lf %lf\n", a[i].id, e[i], a[i].s, a[i].v0);
  87. }
  88. printf("%.10lf\n", ans);
  89. }

@details@

虽然题目只给了 n 个滑行道,但滑行道之间那些“运行速度为 0 的滑行道”也有 n + 1 个,所以总共 2*n 个滑行道。因此线段树要开到 8*MAXN。

因为这种很 sb 的原因 RE 了一次。。。

@codeforces - 1209H@ Moving Walkways的更多相关文章

  1. Codeforces 1311F Moving Points

    题目链接 根据题意,d是两个点的最短距离,分析知,假设\(x_i\)<\(x_j\), 若\(v_i\)>\(v_j\),那么d(i,j)一定为0,因为i一定能追上j,否则,d(i,j)就 ...

  2. Codeforces Round #624 (Div. 3) F. Moving Points 题解

    第一次写博客 ,请多指教! 翻了翻前面的题解发现都是用树状数组来做,这里更新一个 线段树+离散化的做法: 其实这道题是没有必要用线段树的,树状数组就能够解决.但是个人感觉把线段树用熟了会比树状数组更有 ...

  3. 详细讲解Codeforces Round #624 (Div. 3) F. Moving Points

    题意:给定n个点的初始坐标x和速度v(保证n个点的初始坐标互不相同), d(i,j)是第i个和第j个点之间任意某个时刻的最小距离,求出n个点中任意一对点的d(i,j)的总和. 题解:可以理解,两个点中 ...

  4. Codeforces Round #693 (Div. 3) G. Moving to the Capital (图,dp)

    题意:有一张有向图,每个点的权值为点\(1\)到该点的最短距离(每条边的长度为\(1\)),对于一条路径,这条路径上最多只能有一条边,这条边起点的权值不小于终点,现在要求每个点能到达路径上的点的最小权 ...

  5. Codeforces Round #582 (Div. 3) A. Chips Moving

    传送门 题解: 给你n个数的坐标,你需要把他们移动到一个位置,有两种移动方式 1.向左或者右移动2 2.向左或者右移动1,但是耗费1 求最小耗费 题解: 很简单就可以想到,看一下偶数坐标多还是奇数坐标 ...

  6. Codeforces 586D. Phillip and Trains 搜索

    D. Phillip and Trains time limit per test: 1 second memory limit per test :256 megabytes input: stan ...

  7. codeforces 519E A and B and Lecture Rooms LCA倍增

    Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Prac ...

  8. [codeforces 293]A. Weird Game

    [codeforces 293]A. Weird Game 试题描述 Yaroslav, Andrey and Roman can play cubes for hours and hours. Bu ...

  9. CodeForces 222B Cosmic Tables

    Cosmic Tables Time Limit:3000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Subm ...

随机推荐

  1. CSS--去除除文本基线的几种方式

    削除文本基线的几种方式:1.display:block2.vertical-align:middle3.font-size:0px

  2. vmware三种网络模式配置(转载)

    虚拟机系统安装的是Linux系统 首先,在本机上查看所有网络配置连接,使用命令:ipconfig Microsoft Windows [版本 6.1.7600]版权所有 (c) 2009 Micros ...

  3. JDBC 操作数据库实例

    JDBC是什么 JDBC代表Java数据库连接(Java Database Connectivity),它是用于Java编程语言和数据库之间的数据库无关连接的标准Java API,换句话说:JDBC是 ...

  4. python基本算法题(一)

    1.3位水仙花数计算 "3位水仙花数”是指一个三位整数,其各位数字的3次方和等于该数本身. 例如: ABC是一个“3位水仙花数”,则:A的3次方+B的3次方+C的3次方 = ABC. 使用P ...

  5. VMware ESXi 6.7服务器设置开机自动启动虚拟机

    VMware ESXi 6.7服务器设置开机自动启动虚拟机,具体操作步骤如下 1.登陆到VMware ESXi 6.7  web 界面 2.导航器-->主机-->管理  将自动启动修改为 ...

  6. 杨柳絮-Info:对抗杨柳絮的7种方法和2种防治手段

    ylbtech-杨柳絮-Info:对抗杨柳絮的7种方法和2种防治手段 园林养护人员在对抗杨柳絮上 主要有以下两种方法↓↓ 1.化学方法 化学方法是通过激素等调节剂来抑制植物发芽分化,达到减少杨柳开花的 ...

  7. 常见的php攻击(6种攻击详解)

    1.SQL注入 SQL注入是一种恶意攻击,用户利用在表单字段输入SQL语句的方式来影响正常的SQL执行.还有一种是通过system()或exec()命令注入的,它具有相同的SQL注入机制,但只针对sh ...

  8. Spring Boot自动配置原理(转)

    第3章 Spring Boot自动配置原理 3.1 SpringBoot的核心组件模块 首先,我们来简单统计一下SpringBoot核心工程的源码java文件数量: 我们cd到spring-boot- ...

  9. PHP基础-生成静态html页面原理是怎样

    设置example.html为模板文件,然后按照此模板文件生成article-1.html~article-5.html,以此来做简单的演示,代码如下: <?php//将数据存入二维数组$con ...

  10. VM虚拟机下安装无线网卡教程

    前言: 由于最近学习olsrd需要,然后需要无线网卡支持.所以将教程分享如下. 实体机:Windows 7 虚拟机:Ubuntu 14.04 无线网卡:Tenda W311M V3.0 虚拟机软件:V ...