P7599-[APIO2021]雨林跳跃【二分,倍增,ST表】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P7599


1|1题目大意

n棵树,在某棵树上时可以选择向左右两边第一棵比它高的树跳,现在q次询问从[A,B]中某个点出发跳到[C,D]中某个点的最少次数。

1n2×105


1|2解题思路

考虑到主要的阈值[B+1,C1]中的最大值,一旦超过了这个值就只需要考虑是否大于[C,D]中的最大值就好了。

那么我们考虑如何选取起点,首先我们显然不能超过[C,D]中的最大值mx,在这一情况下我们需要找到一个最大的位置小于mx且在它后面没有比mx大的数(否则就跳不过去了),这个过程我们用二分加ST表可以实现。

然后考虑选取起点如何跳跃,首先如果[B+1,C1]中的最大值lim远大于起点的话我们肯定是优先考虑跳左右两边高的那个点,这样肯定是最优的。所以我们在不大于lim的情况下肯定是选取这种跳法。对于这样的跳跃我们处理出一棵树然后在上面倍增即可。

然后当跳到最后一个小于lim的值时我们有两种方法,一种是继续这样跳此时我们的值大于lim了可以直接跳过[B+1,C1]但是需要判断这个值是否小于mx。(否则就顺便跳过了[C,D])。

第二种方法是直接一直往右边跳直到到[C,D]段,这个我们的处理方法可以每个点向它右边比它大的第一个点连边,然后lim肯定在起点到根的路径上,直接用深度减去即可。

记得判无解

时间复杂度O(nlogn)


1|3code

#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; //}

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15025811.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(58)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示