zoukankan      html  css  js  c++  java
  • 【2018.9.20】JOI 2017 Final T2「準急電車 / Semiexpress」

    题目描述

    JOI 铁路公司是 JOI 国唯一的铁路公司。

    在某条铁路沿线共有 $N$ 座车站,依次编号为 $1...N$。 目前,正在服役的车次按照运行速度可分为两类:高速电车(简称快车)与普通电车(简称慢车)。

    • 慢车每站都停。乘慢车时,对于任意一座车站 $i(1⩽i<N)$,车站 $i$ 到车站$ i+1$ 用时均为 $A$。
    • 快车只在车站 $S_1, S_2, ldots, S_M$​​ 停车 $(1=S_1<S_2<cdots<S_M=N)$。乘快车时,对于任意一座车站 $i(1leqslant i<N)$,车站 $i$ 到车站 $i+1$ 用时均为 $B$。

    JOI 铁路公司现拟开设第三类车次:准高速电车(简称准快车)。乘准快车时,对于任意一座车站 $i(1leqslant i<N)$,车站 $i$ 到车站 $i+1$ 用时均为 $C$。准快车的停站点尚未确定,但满足以下条件:

    • 快车在哪些站停车,准快车就得在哪些站停车。
    • 准快车必须恰好有 $K$ 个停站点。

    JOI 铁路公司希望,在 $T$ 分钟内(不含换乘时间),车站 $1$ 可以抵达的车站(不含车站 $1$)的数量 尽可能多。但是,后经过的车站的编号 必须比 先经过的车站的编号 大。

    求出在 $T$ 分钟内,可抵达车站的最大数目。

    输入格式

    第一行有三个整数 $N, M, K$,用空格分隔。
    第二行有三个整数 $A, B, C$,用空格分隔。
    第三行有一个整数 $T$。
    在接下来的 $M$ 行中,第 iii 行有一个整数 $S_i$​​。
    输入的所有数的含义见题目描述。

    输出格式

    一行,一个整数,表示在 $T$ 分钟内,可抵达车站的最大数目。

    样例

    样例输入 1

    10 3 5
    10 3 5
    30
    1
    6
    10

    样例输出 1

    8

    样例解释 1

    在这组样例中,这条铁路上有 $10$ 个车站,快车在车站 $1, 6, 10$ 停车。如果准快车在车站 $1, 5, 6, 8, 10$ 停车,除车站⑨外的其它所有车站都可在 $30$ 分钟内到达。
    以下是从地点 $1$ 到达某些站点的最快方案:

    • 到达车站 $3$:乘坐慢车,耗时 $20$ 分钟。
    • 到达车站 $7$:先乘坐快车,在车站 $6$ 转慢车,耗时 $25$ 分钟。
    • 到达车站 $8$:先乘坐快车,在车站 $6$ 转准快车,耗时 $25$ 分钟。
    • 到达车站⑨:先乘坐快车,在车站 $6$ 转准快车,在车站 $8$ 再转慢车,耗时 $35$ 分钟。

    样例输入 2

    10 3 5
    10 3 5
    25
    1
    6
    10

    样例输出 2

    7

    样例输入 3

    90 10 12
    100000 1000 10000
    10000
    1
    10
    20
    30
    40
    50
    60
    70
    80
    90

    样例输出 3

    2

    样例输入 4

    12 3 4
    10 1 2
    30
    1
    11
    12

    样例输出 4

    8

    样例输入 5

    300 8 16
    345678901 123456789 234567890
    12345678901
    1
    10
    77
    82
    137
    210
    297
    300

    样例输出 5

    72

    样例输入 6

    1000000000 2 3000
    1000000000 1 2
    1000000000
    1
    1000000000

    样例输出 6

    3000

    数据范围与提示

    对于 $18\%$的数据,$Nleqslant 300, K-M=2, Aleqslant 10^6, Tleqslant 10^9$。
    对于另外 $30\%$ 的数据,$Nleqslant 300$。
    对于所有数据,$1leqslant Nleqslant 10^9, 2leqslant Mleqslant Kleqslant 3000, Kleqslant N, 1leqslant B<C<Aleqslant 10^9, 1leqslant Tleqslant 10^{18}$​​, $1=S_1<S_2<cdots<S_M=N$。

    一开始只想了48tps的dp:设dp[i][j][k]表示到达第i个车站,已经新开了j个准快车停车站,且接下来准备换成 慢车(0)/快车(1)/准快车(2)时所需要的时间,最后判断满足$min{dp[i][k-m][0], dp[i][k-m][1], dp[i][k-m][2]} leq T$ $|$ $2<=i<=n$的站数即可(即计算从起点到每个车站的最短时间)。

    然而这个dp显然是错误的,原因是这样没有统一确定准快车的停车站;而且在当前点没有车站/准车站时,最后计算到达当前车站的最短距离时,如果当前车站不是快车/准快车的停车站时,对应的dp项是不能放入min的,因为这一站不停。(不如直接说读错题了)

    那怎么做呢?直觉告诉我们$n=10^9$这样的数据不能dp,也不能常规循环,看来一定要在区间上动手。

    这就可以想到贪心了。

    既然要贪心,就得考虑增设哪些停车站停准快车,以及每个增设的停车站能让多大的车站区间在要求时间T内被到达。

    很显然,对于每个能增设的停车站,应该优先先选影响车站区间大的,即增设这个停车站 能让尽量多的车站能满足在要求时间T内被到达。

    那哪些停车站是备选的?影响区间又有什么突破点?(来欣赏一下我一流的绘画技术)

    上图的$s[i],s[i+1]$表示两个相邻的快车停车站。我们就在每两个快车停车站之间 找找选准快车停车站有没有什么好方法。

    但是在明白之前你得注意到一个题目条件B<C<A。这种条件非常重要,它不是摆给你玩的,相反它打下了这题贪心的基础没这个条件的话这题贪心估计没法做

    这个条件的用处就是可以帮助你想到,从1号车站到达任意一个车站所需的最短时间是什么?

    没错,既然有了上述条件,那肯定先尽可能坐快车,其次尽可能坐准快车,最后坐慢车到达目的地。

    放到图上就是,从1号点出发,先坐快车到离终点最近的快车停车站(1号点到终点间没有快车停车站则不坐),再从这站坐准快车到离终点最近的准快车停车站(这站到终点间没有准快车停车站则不坐),最后从这站坐慢车到终点。

    容易证明,这样就保证了能够优先坐速度快的车,那么到终点站的用时也最短。

    明白了上述道理之后,就开始分析图了:

    图中的黑色竖线表示两个相邻的快车停车站,橙色竖线表示非快车停车站(普通站),红色竖线表示需要考虑选到的准快车停车站

    图中的绿色横线表示从前一个快车停车站开始坐慢车能在要求时间T内到达的车站区间,红色横线表示从左端需要考虑选到的准快车停车站开始坐慢车能在要求时间T内到达的车站区间。

    很明显,在$(s[i],s[i+1])$这段开区间内,被绿色横线覆盖的车站 都可以通过从起点坐快车到快车停车站s[i] 再转慢车在规定时间内坐到。而没有覆盖到的车站就不行,需要另辟准快车停车站。

    但不可能把所有车站都考虑辟为准快车停车站,那样时间复杂度就退化为至少$O(n)$,直接爆。

    一定有一些辟车站的决策是无用的,比如某个车站被辟了之后,明显不如辟另一个车站更优,因此不能考虑。

    所以哪里要考虑辟为准快车停车站?在前一个横线覆盖的区间后的一个点

    为什么要放在这?

    - 如果往前放,就放到前一个已经能够在时限内到达的车站区间了(简称已覆盖区间),可以证明重新覆盖这些车站是没有必要的,因为你希望让已覆盖区间后边的车站(它们还不能在时限内到达)能在时限内到达,那么准快车车站应该尽量靠近后边这些车站,这样就能在其它车站条件相同的情况下 坐更多站的准快车,用更短时间到达那些车站。所以准快车停车站一定会放在后边未覆盖的车站区间,而不应放在前边的已覆盖区间。

    - 如果往后放,中间就会空出几个车站无法在时限内到达。但是中间这些空出的车站本身就具有 距离前一个快车停车站近的优势,如果坐同一种车(准快车),时间相对较短;相反,后边的车站无论如何都要比这些空出的车站多坐几站准快车,那时间就相对较长,转坐慢车会相对更快达到时限,导致在后边放准快车停车站所能覆盖的区间更短(一定要想清楚)。我们希望一个准快车停车站的覆盖区间尽量大,因此根据数学归纳法(其实不用说的这么高端),把准快车停车站放到第一个覆盖不到的车站,再往后转坐慢车时会相对更慢地到达时限,覆盖的区间也就极大,即最优放法。

    上面从左右两个角度论证了在前一个横线覆盖的区间后的一个点辟准快车停车站是最优的。

    所以上图就在相邻两个快车停车站区间内 从左往右依次在第一个覆盖不到的点记为需要考虑选到的准快车停车站,然后将 它能覆盖 且 能对车站产生最优答案(到达时间) 的区间大小记为贡献

    - 有没有注意到,上图中准快车停车站所覆盖的区间大小从左往右是单调不上升的

    确实是这么个结论,由前面说的“如果准快车停车站往右放”一段的论证可以推广证明。

    - 另外,注意到最后一条红线被一些紫线划去了没有?

    最后一个需要考虑的准快车停车站 所覆盖的区间 需要特判,要将它的右端点控制在快车停车站s[i+1]之前,不能计算从s[i+1]开始的贡献。因为根据前面推的先尽可能坐快车,其次尽可能坐准快车,最后坐慢车到达目的地这个结论,s[i+1]及之后的车站可以通过从起点直接坐到快车停车站s[i+1]来更快到达,前面考虑的准快车停车站无法对它们产生更优的答案(到达时间),因此不能算作贡献这就是为什么前面没有直接说“准快车停车站能覆盖的区间大小就是贡献

    以上结论适用于任意两个相邻的快车停车站之间。

    也就是说,在每两个相邻的快车停车站之间,候选准快车停车站所产生的贡献都是单调不上升的。

    那么简化问题,如果只考虑两个相邻的快车停车站区间,从左往右依次取需要考虑选到的准快车停车站,贡献就是从大到小取的了,那么答案也就最优。

    然而多个具有这种性质的相邻快车停车站区间咋办?

    统一从左往右取。就是把每个这样的区间中的第一个需要考虑选到的准快车停车站放入一个优先队列,每次取贡献最大的那个停车站,并把这个停车站所在的相邻快车停车站区间的 下一个要考虑的停车站 放入优先队列 即可。

     1 #include<cstdio>
     2 #include<vector>
     3 #include<algorithm>
     4 
     5 using namespace std;
     6 
     7 const int MAX_M = 3000;
     8 
     9 int N, M, K;
    10 long long A, B, C;
    11 long long T;
    12 int s[MAX_M];
    13 
    14 int nums[3000 * 3000];
    15 int c = 0;
    16 
    17 int getNums(int l, int r){
    18     int res = 0;
    19     long long t = B * l;
    20     int id = l;
    21     int cnt = 0;
    22     while(id < r){
    23         long long x = T - t;
    24         if(x < 0) break;
    25         x /= A;
    26         long long nxt = id + x + 1;
    27         nxt = min((long long)r, nxt);
    28         if(id == l) res = nxt - id;
    29         else nums[c++] = nxt - id;
    30         t += C * (nxt - id);
    31         id = nxt;
    32         cnt++;
    33         if(cnt > K - M) break;
    34     }
    35     return res;
    36 }
    37 
    38 
    39 int solve(){
    40     int ans = B * s[M - 1] <= T ? 1 : 0;
    41     for(int i = 0; i < M - 1; ++i){
    42         ans += getNums(s[i], s[i + 1]);
    43     }
    44     sort(nums, nums + c);
    45     reverse(nums, nums + c);
    46     for(int i = 0; i < K - M; ++i){
    47         ans += nums[i];
    48     }
    49     return ans - 1;
    50 }
    51 
    52 void input(){
    53     scanf("%d%d%d", &N, &M, &K);
    54     scanf("%lld%lld%lld", &A, &B, &C);
    55     scanf("%lld", &T);
    56     for(int i = 0; i < M; ++i){
    57         scanf("%d", s + i);
    58         s[i]--;
    59     }
    60 }
    61 
    62 int main(){
    63     input();
    64     int ans = solve();
    65     printf("%d
    ", ans);
    66     return 0;
    67 }
    View Code
  • 相关阅读:
    complete完成量——实例分析
    worker线程的创建与使用
    SDIO接口
    Linux中V4L2分析
    Linux系统调用分析
    ppm图片显示
    应用层与内核层错误打印函数总结
    高通Sensor驱动学习笔记
    linux中新增一个shell命令的方法
    RTC测试程序
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/9684117.html
Copyright © 2011-2022 走看看