[二分] AT1279 题解

本文讲解了一种优化的算法思路,利用排序和二分查找技巧,解决了一个关于多人在特定时间段内打招呼的问题,将时间复杂度降低至O(nlogn+nlogn),适用于大规模数据。通过实例和代码展示了如何通过先排序开始时间再进行二分查找,高效计算每个人听到的问好次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目传送门

思路

排序+二分

这个题目拿到之后,我们很容易想到 n 2 n^2 n2 的解法,但是看到数据范围,很显然 n 2 n^2 n2 的算法是过不了的,所以我们需要优化。

先来讲一讲 n 2 n^2 n2 的算法

即我们先枚举 1 1 1 n n n 然后再来一层循环 j j j 1 1 1 n n n 。只要保证 j j j 这个人他到这个地方的开始时间大于 i i i 这个人来到这的地方的开始时间且小于 i i i 这个人离开这个地方的结束时间,第 i i i 个人会听到的问好数 + 1 +1 +1

接着我们来讲一讲如何优化

很显然, i i i 的那一层循环肯定是必须要枚举的,所以我们要想办法来优化 j j j 这一层循环。 我们可以先对所有人的开始时间进行排序,排完序之后,我们发现第二层循环可以二分。

怎么二分呢?

因为我们知道的这个序列的开始时间已经有了单调性,那么当然就可以二分了。我们可以运用二分求出这个序列在第 i i i 个人之后最后一个在 i i i 离开之前跟他打招呼的人,然后我们再用这个人在数组的下标减去 i i i 就可以求出 i i i 一共听到了多少声招呼了。

这样的话,时间复杂度是 O ( n l o g n + n l o g n ) O(nlogn+nlogn) O(nlogn+nlogn),是不会超时的。

代码

#include<iostream>
#include<algorithm>
using namespace std;
int n;
struct node
{
    int s,t,id,ans;
    //对左端点的排序规则
    bool operator <(const node &n)const
    {
        return s<n.s||(s==n.s&&t<n.t);
    }
}arr[100005];
//最后根据一开始的输入顺序进行复原的排序。
bool cmp(node x,node y)
{
    return x.id<y.id;
}
int main()
{
	//输入
    cin>>n;
    for(int i=1;i<=n;++i)
    {
        cin>>arr[i].s>>arr[i].t;
        arr[i].id=i;//记得存一开始的下标,不然后面就无法按照题目给定的顺序进行输出了。
    }
    sort(arr+1,arr+n+1);//排序
    for(int i=1;i<=n;++i)
    {
        int l=i,r=n,bj;
        if(arr[i+1].s>arr[i].t)//二分
        {
            continue;
        }
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(arr[mid].s<=arr[i].t)
            {
                bj=mid;
                l=mid+1;
            }
            else
                r=mid-1;
        }
        arr[i].ans=bj-i;//记得减去i
    }
    sort(arr+1,arr+n+1,cmp);//恢复一开始的输入顺序
    for(int i=1;i<=n;++i)
    {
        cout<<arr[i].ans<<endl;//输出
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值