像潮落潮涌,送我奔向自由。|

寂静的海底

园龄:3年2个月粉丝:59关注:15

2022-07-04 20:16阅读: 5评论: 0推荐: 0

【题解】P7599 | 倍增 贪心 分类讨论

0.闲话

做不会的题,要么自己想,要么问一个人,不要同时问几个做法不同的人 / 看几篇做法不同的题解,这些可以等你 AC 后再看,效率会高很多。

总之还是感谢 L7_56 大佬讲解 %% ,虽然没听懂,但是自己磨出来了。

很多题解是 APIO 刚完的时候写的,故讲解不太仔细,在这里补充一下。

1.题目相关

简化题意:

一排树,高度互不相同,每次跳跃会跳到左边第一个比它高的点,或右边第一个比它高的点,多次询问从 [A,B] 中任意点到 [C,D] 中任意点的最少步数,强制在线。

数据范围:

n,q2×105

原题:

lougu P7599

2.约定与记号

本文对树的编号从1开始,在开始和结尾放两棵无限高的树不影响答案。

Hi 表示 i 的高度 , Li 表示 i 左边第一个比它高的点 , Ri 表示 i 右边第一个比它高的点 。

约定 max[A,B] 本文中表示 maxi[A,B]Himin[A,B] 本文中表示 mini[A,B]Hi

特别地, L0=R0=0 , Ln+1=Rn+1=n+1 , H0=Nn+1=+


3.算法,思维过程

首先有 O(nq) BFS ,与正解没啥关系,故不讨论。

1.A=B,C=D

观察特殊数据,考虑对于 A=B,C=D 的特殊数据,思考如何处理。

首先,有解的充要条件是 max[A,C1]HC , 若不满足,一定会经过 高度为 max[A,C1] 的这个点 且无法到达 C , 否则一直向右跳则有解。

若有目前在 点 i(iC) , HLiHC,HRiHC , 则跳到 Li,Ri 中高度更高的点不会更劣。

证明是显然的 , 较低者能跳到的两个边界其中之一是较高者,另一边是 p , 较高者的两个边界之一为 p ,另一边界显然不会更小。

所以一定是会从起点开始,不断向 Li,Ri 中高度更高的点跳跃,直到两者中较高者高度大于等于终点高度。

接下来的最优决策就是一直向右跳,

Ri 大于等于终点高度,又因为有解的条件,此时 Ri 显然是终点

Li 大于等于终点高度(此时显然是大于) , 那么就显然一直向右跳,一定也是满足在不超过终点的高度跳尽可能高。

使用两次倍增 (一次跳尽可能高的点 , 一次跳尽可能右的点 ) 即可解决 。

Rt[i][x] 表示从 i 向右开始跳 2x 步 , Up[i][x] 表示从 i 向尽可能高跳 2x 步 , 然后常规倍增。


2.C=D

先只考虑有解的情况,即 max[B,C1]max[C,D]

接下来考虑起点的选择 , 令 [B,C) 中最大值的位置为 p ,若 p=B 则可以从 p 一步走过去 , 否咋选择一个满足 1 中的条件的一个最高的点 x 作为起点,由于前部分是尽可能向上走,同 1 中的证明,选择高的很明显不劣。

这个点就是 [max{Lp+1,A},B] 中的最大值 , 因为有解且 [Lp+1,p] 中的点都是符合条件的。


3.一般情况

一个特殊的值是 [B,C1] 中的最大值所在点p , 分两种情况进行考虑 , 经过它,不经过。

3.1 经过它

选择 [max{Lp+1,A},B] 中的最大值走过去,同 2 即可 , 走到 p 后一步即可走到。

3.2 不经过它

不经过 p 那么就一定经过 p 左边的某个比它大的点 , 这种情况必须满足 RLp[C,D] , 显然 RLpC

反证:若 RLp>D , 因为不经过 p , 显然是从 p 左边的某个比 p 大的点走过去 , 此时 Lp 是不能到达 [C,D] 的 , Lp 左边的低于 Lp 的要么经过 Lp , 要么只会走到高于 Lp 的点 qRqRLp>D 所以一定满足条件。

若不满足条件,则必须经过 p

Lp[A,B] 则可以一步走到 RLp

否则选取 [A,B] 中的最大值向 Lp 跳即可,然后一步跳到终点区间。

常规倍增(同 1 )即可,见配图。


配图:

无解

1 这种经过 Lp 的路径不行。

2 这种高于 Lp 的点的 R 不会更靠左。

两种路径

1 经过 p
2 不经过 p

使用倍增进行“跳”, ST 表进行 RMQ 查询 , 时空复杂度 O(nlogn)


4.代码实现

关键部分


/*分情况,特判*/

int minimum_jumps(int A, int B, int C, int D) {
    A++,B++,C++,D++;
    if(mx(B,C-1).first>mx(C,D).first) return -1;
    /*判掉无解*/
    auto mx1 = mx(B,C-1);
    if(mx1.second == B) return 1 ;
    int p = mx1.second , Lp = l[p] ,res = 1e9 + 100 ;
    /*过p*/
    int st = mx(max(A,Lp + 1) , B).second ; 
    res = min(res , calc1(st , p) + 1);
    /*到 R[l[p]]*/
    if(r[Lp]>=C&&r[Lp]<=D) {
        if(Lp>=A) res = min(res , 1);
        else res = min(res , calc2(mx(A,B).second,Lp) + 1) ; 
    }
    if(res >= 50000000) return -1;
    return res; 
}

/*两个倍增*/
int calc1(int st,int ed) {
    int ret = 0 ; 
    if(mx(st,ed-1).first > h[ed]) return 110000000 ;
    for(int i = Lg - 1 ; i >= 0 ; -- i) {
        if(h[Up[i][st]]>h[ed]) continue;
        st = Up[i][st] , ret += (1 << i) ;
    }
    debug(st) ; 
    for(int i = Lg - 1 ; i >= 0; -- i) {
        if(Rt[i][st] <= ed && Rt[i][st]) st = Rt[i][st] , ret += (1<<i);
    }
    if(st == ed)return ret;
    return 110000000;
}
int calc2 (int st,int ed) {
    int ret = 0 ;
    if(mx(ed+1,st).first > h[ed]) return 110000000 ;
    for(int i = Lg - 1 ; i >= 0 ; -- i) {
        if(h[Up[i][st]]>h[ed]) continue;
        st = Up[i][st] , ret += (1 << i) ;
    }
    for(int i = Lg - 1 ; i >= 0 ; -- i) {
        if(Lf[i][st] >= ed && Lf[i][st]) st = Lf[i][st] , ret += (1<<i) ; 
    }
    if(st == ed)return ret;
    return 110000000;
}

完整代码

-传送门-

posted @   寂静的海底  阅读(5)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起