这题大多数人用的是二分查找,我想到了一个双指针的写法,虽然综合时间复杂度仍然是的,但二分写法实际上是
,而该写法可以优化成
,即对于T次询问,总时间复杂度是
。
首先先对区间左端点排序,对于一个询问x,我们不做在线回答,而是收集起来做离线回答。 先对询问进行排序。
记当前的询问是 ,当前的区间是
,有以下几种情况
:说明x正中下怀,只需要将答案记录为
即可(
从0开始)
:由于区间被排序了,若当前的x连左端点都够不到,说明往后都不会够到了,故答案是-1
:即当前的
很大,能够越过
的右端点,此时需要i++,也就是寻找下一个左端点更强的区间,才能保证右端点能够包住
特别地: 不需要重新枚举,因为越大的数,对区间的要求越高,即若当前的数字符合,往后一个询问(即我们排过序的询问集合)只会要求比这个不差的区间(至少是i,不可能是比i小的下标)
另外:当,且当前询问还没结束,而i只有区间不够的时候才会++,说明在此之后的,更大的询问数字不再可能落入这个区间(当前的询问都已经超出了这个最强的区间,往后的也一定会)
因此, 是单调不降的,最差的情况是
,而i与T是同数量级的,故可简化为
代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct node{
int l,r,idx;
};
const int N=2e5+7;
vector<node> a;
bool cmp(node a,node b){
return a.l<b.l;
}
int n,q;
vector<pair<int,int>> qu;
int ans[N];
int main(){
ios::sync_with_stdio(false),cin.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++){
int l,r;
cin>>l>>r;
a.push_back({l,r,i});
}
sort(a.begin(),a.end(),cmp);
for(int i=1;i<=q;i++){
int x;
cin>>x;
qu.push_back({x,i});
}
sort(qu.begin(),qu.end());
int i=0;
for(int j=0;j<qu.size();){
int x=qu[j].first;
int y=qu[j].second;
if(i==n){
ans[y]=-1; //如果当前的数字已经走到了最末尾,说明往后的数字都是大于右端点的
j++;//继续
continue;
}
if(a[i].l<=x && x<=a[i].r){ //如果当前恰好满足
ans[y]=a[i].idx;
j++;//继续
}
else if(x<a[i].l){ //如果当前的数小于当前区间端点,往后都不可能符合
ans[y]=-1;
j++;//继续
}
else if(x>a[i].r){ //如果当前的数大于当前区间右端点,需要往后移动
while(i<n && x>a[i].r)i++;
}
}
for(int i=1;i<=q;i++)cout<<ans[i]<<"\n";
return 0;
}
事实上,这个写法写蠢了(为了牺牲可读性),另外一种聪明的写法可以减少常数(AI写的)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct node {
int l, r, idx;
};
// 存储查询:值x + 查询的原始下标(用于按输入顺序输出)
struct Query {
int x, id;
};
const int N = 2e5 + 7;
vector<node> a;
vector<Query> qs;
vector<int> ans; // 存储每个查询的答案
// 区间按l升序排序
bool cmp_node(node a, node b) {
return a.l < b.l;
}
// 查询按x升序排序
bool cmp_query(Query a, Query b) {
return a.x < b.x;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n, q;
cin >> n >> q;
// 输入区间
for (int i = 1; i <= n; i++) {
int l, r;
cin >> l >> r;
a.push_back({l, r, i});
}
sort(a.begin(), a.end(), cmp_node);
// 输入查询,记录原始下标(用于按输入顺序输出)
qs.resize(q);
ans.resize(q, -1); // 默认答案为-1
for (int i = 0; i < q; i++) {
cin >> qs[i].x;
qs[i].id = i;
}
sort(qs.begin(), qs.end(), cmp_query);
// 双指针遍历:i遍历区间,j遍历查询
int i = 0;
for (auto& query : qs) {
int x = query.x;
int qid = query.id;
// 移动区间指针:找到所有l ≤ x的区间,检查是否包含x
while (i < n) {
if (a[i].l > x) {
// 后续区间的l更大,不可能包含x,跳出
break;
}
if (a[i].r >= x) {
// 找到第一个包含x的区间,记录答案并跳出
ans[qid] = a[i].idx;
break;
}
// 当前区间l≤x但r<x,继续看下一个区间
i++;
}
// 若i==n,说明没有区间包含x,ans[qid]保持-1
}
// 按原始输入顺序输出答案
for (int x : ans) {
cout << x << "\n";
}
return 0;
}



京公网安备 11010502036488号