二分(老死不相往来)

马孔多是一个奇怪的小镇,镇上的房子沿着一条河流的南岸而建,而且镇上的居民一辈子都只在自家附近一个固定半径的范围内活动,有些居民永远不会相互接触,即使他们生活一辈子也老死不相往来。 

马孔多小镇一共有n座房子,以到镇子的西端的距离算,居民家的位置为p,他们活动的范围为r,请问马孔多小镇一共会有多少对住户之间老死不相往来。

输入

第1行:一个数N,表示房子的数量(1 <= N <= 50000)
第2 - N + 1行:每行2个数P, R中间用空格分隔,P表示房子的位置,R表示这家住户的活动范围半
径(1 <= P, R <= 10^9)

输出

输出共有多少对老死不相往来的住户。

样例输入 Copy

4
1 1
2 1
3 2
4 1

样例输出 Copy

1

提示

1. 样例解释
4座房子分别位于1, 2, 3, 4的位置,活动范围半径分别为1, 1, 2, 1,那么{1, 2}, {1, 3} {2, 3} {2, 4} {3,4}这5对居民活动范围都有交点,只有{1, 4}是老死不相往来的。
2. 数据范围
对于10%的数据,1≤N≤10;
对于40%的数据,1≤N≤2000;
对于100%的数据,1≤N≤50000。
题目解析:

我们可以把每一家居民的活动范围看做圆,对于一个圆,我们可以确定它在直线上的一条直径

[l,r],如果两个圆的直径没有交点,则可以认为这两个圆相离。


我们用l[]r[]分别记录每个圆直径的左右端点,分别排序。从左到右遍历每个r[i],存在j使得

l[j]>r[i]则说明j以及l值更靠右的圆都与i相离。

排序复杂度O(nlogn);每个i比较端点使用二分复杂度为O(logn),整体O(nlogn);因此总复杂度

O(nlogn)

后面的二分部分还可以优化到O(n),即将所有线段的起点和终点混合排序,然后逐个访问每一个点。对于每一个起点,只要记录他前面有多少个终点即可。排序O(nlogn),遍历O(n),总的复杂度为O(nlogn)

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e7+10;
struct node{
    int l,r;
};
node a[maxn];
bool cmp(node x,node y){
    return x.l < y.l ;
}
int ef(int l,int r,int x){
    int ans;
    while(l<=r){
        int mid=(l+r)/2;
        if(a[mid].l<x){
            l=mid+1;
        }
        else if(a[mid].l>=x){
            r=mid-1;        
        }
    }
    return l;
}
int main()
{
    int n,x,y;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>x>>y;
        a[i].l=x-y;
        a[i].r=x+y;
    }
    sort(a,a+n,cmp);
    int ans=0;
    for(int i=0;i<n-1;i++){
        ans+=n-ef(i+1,n-1,a[i].r+1);//二分找与第i个相离的最小的那个居民 
    }                  
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lipu123/p/12170160.html