P1001 风眼定位 官方题解
风眼定位
【题意简述】
存在一个长度为 、元素互不相同的隐藏序列。调用 可以得到区间 中次大元素在原序列中的位置。要求在不超过 次询问内返回全局最大元素的位置。
【Hint】
【提示】
先询问整个区间得到全局次大值的位置 。只要后续询问区间同时包含 与全局最大值,返回值就一定仍为 。
【数据点 111】
当 时,先询问整个区间得到全局次大值位置 ,再逐步缩短包含 的区间,排除不可能成为最大值的位置。即使线性缩短区间,询问次数也不会超过 。
这一档已经给出最重要的判断:若某个询问区间包含 ,那么返回 当且仅当这个区间也包含全局最大值。
【数据点 222】
特殊性质保证最大值位于端点。询问 得到 后,最大值只可能在 或 。若询问 返回 ,说明最大值在左侧,答案为 ;否则答案为 。
【正解】
首先询问 ,记返回位置为 。若 ,最大值只能位于右侧;若 ,最大值只能位于左侧。
当 时,询问 。若返回 ,则区间中同时包含全局最大值与全局次大值,最大值位于 左侧;否则最大值位于 右侧。
接下来只需要在已经确定的一侧二分,并保证每次询问都包含 。
【最大值位于左侧】
维护答案属于 。取上中点
并询问 。
- 若返回 ,说明最大值位于 ,令 ;
- 否则最大值位于 ,令 。
【最大值位于右侧】
维护答案属于 。取下中点
并询问 。
- 若返回 ,说明最大值位于 ,令 ;
- 否则最大值位于 ,令 。
两种二分都始终保留真实答案,并严格缩短区间,所以最终 时得到全局最大值位置。
【询问次数】
第一次询问得到 ,至多再用一次询问判断方向。二分长度不超过 ,所以总询问次数不超过
当 时,这个数量远小于 。额外空间复杂度为 。
【参考代码】
/*Author:EhundateghDate:2026/7/30Name:locate.cppYou steal,I kill.*/#include "locate.h"using namespace std; void Init(int c,int T){ return;} int Locate(int c,int n){ int Second=Query(1,n); bool Left; if(Second==1) Left=false; else if(Second==n) Left=true; else Left=(Query(1,Second)==Second); if(Left){ int l=1,r=Second-1; while(l<r){ int Mid=(l+r+1)>>1; if(Query(Mid,Second)==Second) l=Mid; else r=Mid-1; } return l; } int l=Second+1,r=n; while(l<r){ int Mid=(l+r)>>1; if(Query(Second,Mid)==Second) r=Mid; else l=Mid+1; } return l;}