题目大意:
平面上N个两两没有公共点的圆,i号圆的半径为ri,圆心在(xi,yi),求所有在最外层,即不被包含在其他圆内部的圆 。
解题思路:
注意所给的圆都没有公共点。
维护一个set,里面保存着和扫描线相交的圆,再用扫描线从左往右扫。
如果扫到某圆的最左点,当它不被其他圆包含时,就把它加入到set中。
如果扫到是某圆的最右点,并且该圆在数据结构中,就把这个圆删掉。
怎么检查它有没有被其他圆包含呢?set里的圆(注意是不被其他圆覆盖的)互不相交,也就是说他们在当前的扫描线上占的区间也互不相交, 我们可以只记录圆心在扫描线的投影, 把低的投影称为前,把高的投影低称为后,那么可能覆盖当前圆的只可能是它的前一个圆或后一个圆。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<queue>
#include<vector>
#include<set>
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=40005;
const double eps=1e-8;
struct cone
{
double r,x,y;
inline friend bool operator < (const cone &a,const cone &b)
{
if(a.x==b.x&&a.y==b.y)return a.r<b.r;
if(a.y==b.y)return a.x<b.x;
return a.y<b.y;
}
}a[N];
struct point
{
double p;
int id,f;
inline friend bool operator < (const point &a,const point &b)
{
if(a.p==b.p)return a.f>b.f;
return a.p<b.p;
}
}b[N<<1];
int n,m,cnt;
bool exist[N];
set<cone>S;
set<cone>::iterator it;
bool in(cone u,cone v)
{
return (u.x-v.x)*(u.x-v.x)+(u.y-v.y)*(u.y-v.y)<=v.r*v.r;
}
int main()
{
//freopen("lx.in","r",stdin);
//freopen("lx.out","w",stdout);
n=getint();
for(int i=1;i<=n;i++)
{
scanf("%lf%lf%lf",&a[i].r,&a[i].x,&a[i].y);
b[++m].p=a[i].x-a[i].r,b[m].id=i,b[m].f=1;
b[++m].p=a[i].x+a[i].r,b[m].id=i,b[m].f=0;
}
sort(b+1,b+m+1);
for(int i=1;i<=m;i++)
{
int x=b[i].id;
if(b[i].f==1)
{
S.insert(a[x]);
it=S.find(a[x]);
if(it!=S.begin())
{
cone t=*--it;++it;
if(in(a[x],t))
{
S.erase(it);
continue;
}
}
if(++it!=S.end())
{
cone t=*it;--it;
if(in(a[x],t))
{
S.erase(it);
continue;
}
}
exist[x]=true,cnt++;
}
else if(exist[x])
{
it=S.find(a[x]);
S.erase(it);
}
}
cout<<cnt<<'\n';
for(int i=1;i<=n;i++)
if(exist[i])cout<<i<<" ";
return 0;
}