P7599-[APIO2021]雨林跳跃【二分,倍增,ST表】
正題
題目鏈接:https://www.luogu.com.cn/problem/P7599
題目大意
nnn棵樹,在某棵樹上時(shí)可以選擇向左右兩邊第一棵比它高的樹跳,現(xiàn)在qqq次詢問從[A,B][A,B][A,B]中某個(gè)點(diǎn)出發(fā)跳到[C,D][C,D][C,D]中某個(gè)點(diǎn)的最少次數(shù)。
1≤n≤2×1051\leq n\leq 2\times 10^51≤n≤2×105
解題思路
考慮到主要的閾值[B+1,C?1][B+1,C-1][B+1,C?1]中的最大值,一旦超過了這個(gè)值就只需要考慮是否大于[C,D][C,D][C,D]中的最大值就好了。
那么我們考慮如何選取起點(diǎn),首先我們顯然不能超過[C,D][C,D][C,D]中的最大值mxmxmx,在這一情況下我們需要找到一個(gè)最大的位置小于mxmxmx且在它后面沒有比mxmxmx大的數(shù)(否則就跳不過去了),這個(gè)過程我們用二分加STSTST表可以實(shí)現(xiàn)。
然后考慮選取起點(diǎn)如何跳躍,首先如果[B+1,C?1][B+1,C-1][B+1,C?1]中的最大值limlimlim遠(yuǎn)大于起點(diǎn)的話我們肯定是優(yōu)先考慮跳左右兩邊高的那個(gè)點(diǎn),這樣肯定是最優(yōu)的。所以我們在不大于limlimlim的情況下肯定是選取這種跳法。對于這樣的跳躍我們處理出一棵樹然后在上面倍增即可。
然后當(dāng)跳到最后一個(gè)小于limlimlim的值時(shí)我們有兩種方法,一種是繼續(xù)這樣跳此時(shí)我們的值大于limlimlim了可以直接跳過[B+1,C?1][B+1,C-1][B+1,C?1]但是需要判斷這個(gè)值是否小于mxmxmx。(否則就順便跳過了[C,D][C,D][C,D])。
第二種方法是直接一直往右邊跳直到到[C,D][C,D][C,D]段,這個(gè)我們的處理方法可以每個(gè)點(diǎn)向它右邊比它大的第一個(gè)點(diǎn)連邊,然后limlimlim肯定在起點(diǎn)到根的路徑上,直接用深度減去即可。
記得判無解
時(shí)間復(fù)雜度O(nlog?n)O(n\log n)O(nlogn)
code
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; const int N=2e5+10,T=18; int n,q,h[N],p[N],l[N],r[N],lg[N]; int f[N][T],g[N][T],dep[N]; int RMQ(int l,int r){if(l>r)return 0;int z=lg[r-l+1];return max(f[l][z],f[r-(1<<z)+1][z]); } void init(int N,std::vector<int> H) {n=N;h[0]=n+1;for(int i=1;i<=n;i++)h[i]=H[i-1],p[h[i]]=i,f[i][0]=h[i];for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;for(int j=1;(1<<j)<=n;j++)for(int i=1;i+(1<<j)-1<=n;i++)f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);for(int i=1;i<=n;i++)l[i]=i-1,r[i]=i+1;for(int i=1;i<=n;i++){int x=p[i];r[l[x]]=r[x];l[r[x]]=l[x];g[x][0]=(h[l[x]]>h[r[x]])?l[x]:r[x];}for(int j=1;j<T;j++)for(int i=1;i<=n;i++)g[i][j]=g[g[i][j-1]][j-1];for(int i=n;i>=1;i--)dep[i]=dep[r[i]]+1; } int minimum_jumps(int A,int B,int C,int D){A++;B++;C++;D++;int l=A,r=B;int lim=RMQ(B+1,C-1),zc=RMQ(C,D);if(RMQ(B,C-1)>zc)return -1;while(l<=r){int mid=(l+r)>>1;if(RMQ(mid,B)>zc)l=mid+1;else r=mid-1;}int x=p[RMQ(l,B)],ans=0;if(h[x]>lim)return 1;for(int i=T-1;i>=0;i--)if(h[g[x][i]]<lim)x=g[x][i],ans+=(1<<i);if(g[x][0]&&h[g[x][0]]<zc&&h[x]<lim)ans+=2;else ans+=dep[x]-dep[p[lim]]+1;return ans; } //vector<int> H; //int main() //{ // scanf("%d%d",&n,&q); // for(int i=0,x;i<n;i++) // scanf("%d",&x),H.push_back(x); // init(n,H); // while(q--){ // int A,B,C,D; // scanf("%d%d%d%d",&A,&B,&C,&D); // printf("%d\n",minimum_jumps(A,B,C,D)); // } // return 0; //}總結(jié)
以上是生活随笔為你收集整理的P7599-[APIO2021]雨林跳跃【二分,倍增,ST表】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 宝贝去哪儿歌词 歌曲宝贝去哪儿歌词
- 下一篇: 美不胜收的胜是什么意思