2021.09.16pm

---- 预期 实际
A 100 50
B 100 0
C 50 50
D 100 90
E 100 100
F 100 50
S 550 340

可能水,一定菜

SPECIAL 赛车([CEOI 2003] The Race) \(\blacktriangle\!\blacktriangledown\!\blacktriangle\)

  1. 作为基础题的最后一道,我觉得有点委屈它。
  2. 这道题之前做过,并且做对了。但之前写的过于冗杂且效率不高。
  3. 这道题询问了两个问题,看似有联系,实则得分开求解,原因在于超车事件最多只输出 \(10000\)

Q1

  1. 第一问是相对容易解决的。会发生超车,是因为在后面的车跑的比你快,或者前面的车比你跑的慢。这不就是逆序对吗。用树状数组找就解决了。
  2. 但这里有一个小差别:用距离当第一关键词还是用速度当第一关键词。我们希望时间和空间尽量小,那就选范围大的作为第一关键词排序(同时距离也不会重复),时间复杂度和空间复杂度均从 \(O(n \log n+x \log x)\) 优化到 \(O(n \log n+v \log v)\)

Q2

  1. 定理一:\(A\) 超过 \(B\) ,则 \(B\) 不可能再超过 \(A\)
  2. 定理二: \(A\) 前方分别是 \(B\)\(C\) 则,\(A\) 必须先超过 \(B\) 再超过 \(C\),或必须 \(B\) 超过 \(C\)\(A\) 才可能超过 \(C\),\(or\) ,只能先超过相邻的人或被相邻的人超过
  3. 证明:容易证明(这里写不下)
  4. 解这道题这两个定理就足够了。
  5. 我们先找每一个人 \(A\) 前方的第一个人 \(B\),如果能超车就放进优先队列,按照追上时间为第一关键字,追上位置为第二关键字降序排序(\(P.S.\) 某大佬想通过推导式子找出排序依据,并发出了 \(double\) 狗都不用的著名言论,并在改成double后A了此题 )
  6. 每次\(push\)同时维护目前的位置状态,把新产生的两组可能追上的情况(\(A.B.C.D. \to A.C.B.D\),故可能 \(C\) 追上 \(A\) 和 可能 \(D\) 追上\(A\))
  7. 有两个小问题
    • 一个是怎么堆外维护目前状态。
      • 可以用链表(但是我写挂了,也和我的其他实现方式有关)
      • 亦可以用映射。一个数组维护每辆车对应的距离排名,另一个数组维护总体的状态(以查找前后的车辆)
    • 另一个是排重
      • 为什么会有这个问题:\(A.B.C.D. \to A.B.D.C. \to B.A.D.C. \to B.D.A.C.\to D.B.A.C.\)
      • 可以发现第二次和第三次\(D\)\(B\) 都相邻,那么可能都会扔进堆里。
      • 解决方法也很多,可以\(hash\),但是难写。题目中给出了不会在同一位置出现超车,我们第二关键字就是超车位置,那和前一个出堆的相同跳过答案就不会重复了

A NOIP题海战 \(\blacktriangle\)

  1. 这道题主要是数据恶心。最后一行没给全,这样我的代码就会有问题。
  2. 就是道模拟,暴力都能过

B 有序表的最小和 \(\blacktriangle\!\blacktriangledown\)

  1. 维护一个堆,先把\(a[1]+b[i]\)全部放进去,每次取出时更新\(a[i] \to a[i+1]\) 再放进堆即可

C 木板 \(\blacktriangle\!\blacktriangledown\!\blacktriangle\)

  1. 这道题很高兴,因为只会打暴力DP
  2. 严格复杂度 \(O(n^2)\) ,但这道题稍微优化一下能过掉
  3. \(DP\) 方程:

\[ DP[i]=\min(DP[i-1]+ch) \]

  1. 表示已经拼了\(i-1\) 个木板,转移到拼 \(i\) 个木板,右端点最靠左是多少。(背包)
  2. 优化:先排序,从已知成立的位置开始向下以此枚举,枚举到第一个非最优的情况(再往下就更不可能是最优的)
  3. \(tag\) 是优先队列,但据说正解是贪心,这究竟是什么题

D 区间和 \(\blacktriangle\)

  1. 树状数组
  2. 听说你没开 \(long long\)?(黑人抬棺)

E 堆排序 \(\blacktriangle\)

  1. \(RT\)

F 双端队列 \(\blacktriangle\!\blacktriangledown\)

  1. 之前做过,一遍对,复杂度\(O(n+n\log n)\),但是这次却写挂了···
  2. 我们可以把双端队列看成一个奇怪的队列。因为最后要拼成一个递增的数组,那么我们完全可以先按数值进行排序,从最小的开始以此选,看能否放进同一个双端队列,不能则再开一个
  3. 那么问题在于什么情况下必须新开一个双端队列?(我们先考虑没有重复元素的情况)
    • 因为当前队列必须放入剩下的最小的元素,而放下之后就只能从一端进队列了,也就是说,位置在最小元素之前的用的是双端队列,之后的是队列。
    • 上方虽然用的是双端队列,我们依然可以看成是队列,但加入顺序是从最小元素的位置先向上扫,再从最上面往下扫。也就是说,从当前最小的元素开始,依次加入大一点的元素,如果满足当前趋势(若在下侧,则只能一直向下;若在上侧,先向上扫再向下扫)则加入这个队列,不满足,则重新开一个。
    • 对于相同元素,我们按照出现先后顺序进行排序,目的是能找到相同区间的左右端点,来判断目前趋势是否改变。
      \(\cal CODE:\)
inline int suan(int fang){
    int ans=1;
    int maxn=0;
    int i=1,j;
    while(p[i+1].va==p[i].va)i++;//i,j是区间两个端点
    maxn=p[i].num;
    i++;
    for(;i<=n;i++){
        j=i;
        while(p[j+1].va==p[i].va)j++;
        if(fang==2){//向上
            if(maxn>p[j].num)maxn=p[i].num;
            else{
                maxn=p[j].num;
                fang^=1;
            }
        }
        else{//趋势向下
            if(maxn<p[i].num)maxn=p[j].num;
            else{
                ans++;
                maxn=p[i].num;
                fang^=1;
            }
        }
        i=j;
    }
    return ans;
}

image
\(\cal {Made} \ {by} \ {YuGe}\)

posted @ 2021-09-17 17:29  u2003  阅读(25)  评论(0)    收藏  举报