思路
排序+二分
这个题目拿到之后,我们很容易想到 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;//输出
}
}