题目意思:
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1278
第1行:一个数N,表示圆的数量(1 <= N <= 50000)
第2 - N + 1行:每行2个数P, R中间用空格分隔,P表示圆心的位置,R表示圆的半径(1 <= P, R <= 10^9)
输出共有多少对相离的圆。
4
1 1
2 1
3 2
4 1
1
题目分析:
/**
*方法一
*将圆分为左右短点,用线段表示,现在就变形为了线段相交问题
*左端点left=c-r,右端点right=c+r,按左端点升序排序,相等的话
按右端点升序排序,然或进行扫描,对于每一条线段i,当以后的某一条
*线段j的左端点大于i的右端点,则以后的所有线段与其(i)相离
*时间复杂度为O(nlog(n)+n^2)
*此题的数据是50000,正常的O(n*n)的时间肯定会超时
*现在我们可以考虑改变步长的方法优化算法(只能降低系数,但有时确实很好),
*我在查询的时候优化,另步长为100,如果当前的j满足条件,则最先满足
*条件的j一定在j之前的100个以内,只需要在查询j的前100个即可,
*时间复杂度O(n*log(n)+n*(n/100+100));对于本题将你n^2的系数降低了尽100倍
*************************************************************************
*方法二 感谢Dava
*把所有圆的起点和终点放在一个数组中,按大小排序(圆的左右短点表示)
*如果某个圆的起点与另一个圆终点大小相等,则把起点排在前面
*定义结构体
*struct{
* long long d;//线段的节点
* int tmp;//标记起点(0)和终点(1),
*}
*记录所有的起点和终点直接快排,
*令num=圆的个数,遇到起点num--,遇到终点sum+=num
*/
AC代码,方法1
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
struct node{
int left,right;
};
node p[50005];
int cmp(node a,node b){
if(a.left<b.left) return 1;
else if(a.left==b.left&&a.right<b.right) return 1;
return 0;
}
int main()
{
int c,r,n;
while(cin>>n){
for(int i=0;i<n;i++){
cin>>c>>r;
p[i].left=c-r;
p[i].right=c+r;
}
sort(p,p+n,cmp);
int i,j,sum=0,k;
for(i=0;i<n;i++){
for(j=i+100;j<n;j+=100){//步长为100
if(p[j].left>p[i].right) break;
}
if(j>n) j=n;
for(k=j-1;k>0&&k>j-101;k--){//扫描之间100个元素
if(p[k].left<=p[i].right){
sum+=n-(k+1); break;
}
}
}
cout<<sum<<endl;
}
return 0;
}
AC代码,方法二
#include<iostream>
#include<algorithm>
using namespace std;
struct node{
long long d;//线段的节点
int flag;//标记起点(0)和终点(1),
}p[100005];
int cmp(node a,node b){
if(a.d<b.d) return 1;
else if(a.d==b.d&&a.flag==0) return 1;
return 0;
}
int main()
{
int c,r,n;
while(cin>>n){
int k=0;
for(int i=0;i<n;i++){
cin>>c>>r;
p[k].d=c-r;
p[k++].flag=0;//起点
p[k].d=c+r;
p[k++].flag=1;//终点
}
//cout<<k<<endl;
sort(p,p+k,cmp);
int sum=0,num=n;
for(int i=0;i<k;i++){
//cout<<p[i].d<<" "<<p[i].flag<<endl;
if(p[i].flag==0) num--;
else if(p[i].flag==1) sum+=num;
}
cout<<sum<<endl;
}
return 0;
}