马孔多是一个奇怪的小镇,镇上的房子沿着一条河流的南岸而建,而且镇上的居民一辈子都只在自家附近一个固定半径的范围内活动,有些居民永远不会相互接触,即使他们生活一辈子也老死不相往来。
马孔多小镇一共有n座房子,以到镇子的西端的距离算,居民家的位置为p,他们活动的范围为r,请问马孔多小镇一共会有多少对住户之间老死不相往来。
输入
第1行:一个数N,表示房子的数量(1 <= N <= 50000)
第2 - N + 1行:每行2个数P, R中间用空格分隔,P表示房子的位置,R表示这家住户的活动范围半
径(1 <= P, R <= 10^9)
第2 - N + 1行:每行2个数P, R中间用空格分隔,P表示房子的位置,R表示这家住户的活动范围半
径(1 <= P, R <= 10^9)
输出
输出共有多少对老死不相往来的住户。
样例输入
4
1 1
2 1
3 2
4 1
样例输出
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。
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; }