题目描述

  在平面上,有 \(n\) 个圆,记为 \(c_1,c_2,\ldots,c_n\) 。我们尝试对这些圆运行这个算法:

  1. 找到这些圆中半径最大的。如果有多个半径最大的圆,选择编号最小的。记为 \(c_i\) 。
  2. 删除 \(c_i\) 及与其有交集的所有圆。两个圆有交集当且仅当平面上存在一个点,这个点同时在这两个圆的圆周上或圆内。(原文直译:如果平面上存在一个点被这两个圆所包含,我们称这两个圆有交集。一个点被一个圆包含,当且仅当它位于圆内或圆周上。)
  3. 重复上面两个步骤直到所有的圆都被删除。

  当 \(c_i\) 被删除时,若循环中第1步选择的圆是 \(c_j\) ,我们说 \(c_i\) 被 \(c_j\) 删除。对于每个圆,求出它是被哪一个圆删除的。

  \(n\leq 300000\)

题解

  貌似不太好枚举每个圆,找出剩下的和这个圆相交的圆。

  那就换一种思路。

  枚举每个圆 \(i\),找出第一个与这个圆相交且是作为最大的圆被删掉的圆。

  前面的作为最大的圆被删掉的圆肯定是两两不相交的,且半径大于圆 \(c_i\)。

  那么我们可以对前面的圆维护扫描线,每个圆和当前的直线 \(x=x_0\) 相交两次,可以用括号表示 。

  而且由于这些圆两两不相交,括号相对次序不会变。

  由于前面的圆半径都比它大,因此和它有交的圆必然经过 \(x=x_i+r_i\) 或 \(x=x_i-r_i\) 或 \(y=y_i-r_i\) 或 \(y=y_i+r_i\)。

  所以我们可以在做扫描线时,查询这四个位置的平衡树上,当前圆的前驱后继。

  但是这道题有很多个询问。

  那就加上一个CDQ分治咯。

  时间复杂度:\(O(n\log^2n)\)

  实际上跑的比 k-d tree 还慢很多倍。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
#include<set>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
if(a>b)
swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
char str[100];
sprintf(str,"%s.in",s);
freopen(str,"r",stdin);
sprintf(str,"%s.out",s);
freopen(str,"w",stdout);
#endif
}
int rd()
{
int s=0,c,b=0;
while(((c=getchar())<'0'||c>'9')&&c!='-');
if(c=='-')
{
c=getchar();
b=1;
}
do
{
s=s*10+c-'0';
}
while((c=getchar())>='0'&&c<='9');
return b?-s:s;
}
void put(int x)
{
if(!x)
{
putchar('0');
return;
}
static int c[20];
int t=0;
while(x)
{
c[++t]=x%10;
x/=10;
}
while(t)
putchar(c[t--]+'0');
}
int upmin(int &a,int b)
{
if(b<a)
{
a=b;
return 1;
}
return 0;
}
int upmax(int &a,int b)
{
if(b>a)
{
a=b;
return 1;
}
return 0;
}
const int N=300010;
const int inf=0x7f7f7f7f;
struct circle
{
ll x,y,r;
int id;
}; struct event
{
ll t;
int op;
int v;
event(){}
event(ll a,int b,int c)
{
t=a;
op=b;
v=c;
}
}; int cmp(circle a,circle b)
{
if(a.r!=b.r)
return a.r>b.r;
return a.id<b.id;
} int cmp2(event a,event b)
{
return a.t<b.t;
} int n;
circle a[N];
int ans[N];
int final[N];
int b[N];
int m;
event c[2*N];
set<pii> s; int inter(int x,int y)
{
return (a[x].x-a[y].x)*(a[x].x-a[y].x)+(a[x].y-a[y].y)*(a[x].y-a[y].y)<=(a[x].r+a[y].r)*(a[x].r+a[y].r);
} void solve(int l,int r)
{
if(l==r)
{
if(ans[l]==inf)
{
ans[l]=l;
b[l]=1;
}
return;
}
int mid=(l+r)>>1;
solve(l,mid); m=0;
for(int i=l;i<=mid;i++)
if(b[i])
{
c[++m]=event(3*(a[i].x-a[i].r)-2,1,i);
c[++m]=event(3*(a[i].x+a[i].r),2,i);
}
for(int i=mid+1;i<=r;i++)
{
c[++m]=event(3*(a[i].x-a[i].r)-1,3,i);
c[++m]=event(3*(a[i].x+a[i].r)-1,3,i);
}
sort(c+1,c+m+1,cmp2);
for(int i=1;i<=m;i++)
if(c[i].op==1)
s.insert(pii(a[c[i].v].y,c[i].v));
else if(c[i].op==2)
s.erase(pii(a[c[i].v].y,c[i].v));
else
{
auto it=s.lower_bound(pii(a[c[i].v].y,0));
if(it!=s.end())
{
int x=it->second;
if(inter(x,c[i].v))
ans[c[i].v]=min(ans[c[i].v],x);
}
if(it!=s.begin())
{
it--;
int x=it->second;
if(inter(x,c[i].v))
ans[c[i].v]=min(ans[c[i].v],x);
}
} m=0;
for(int i=l;i<=mid;i++)
if(b[i])
{
c[++m]=event(3*(a[i].y-a[i].r)-2,1,i);
c[++m]=event(3*(a[i].y+a[i].r),2,i);
}
for(int i=mid+1;i<=r;i++)
{
c[++m]=event(3*(a[i].y-a[i].r)-1,3,i);
c[++m]=event(3*(a[i].y+a[i].r)-1,3,i);
}
sort(c+1,c+m+1,cmp2);
for(int i=1;i<=m;i++)
if(c[i].op==1)
s.insert(pii(a[c[i].v].x,c[i].v));
else if(c[i].op==2)
s.erase(pii(a[c[i].v].x,c[i].v));
else
{
auto it=s.lower_bound(pii(a[c[i].v].x,0));
if(it!=s.end())
{
int x=it->second;
if(inter(x,c[i].v))
ans[c[i].v]=min(ans[c[i].v],x);
}
if(it!=s.begin())
{
it--;
int x=it->second;
if(inter(x,c[i].v))
ans[c[i].v]=min(ans[c[i].v],x);
}
} solve(mid+1,r);
} int main()
{
open("circle");
scanf("%d",&n);
ll minx=0x7fffffff,miny=0x7fffffff;
for(int i=1;i<=n;i++)
{
// scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].r);
a[i].x=rd();
a[i].y=rd();
a[i].r=rd();
a[i].id=i;
minx=min(minx,a[i].x);
miny=min(miny,a[i].y);
}
for(int i=1;i<=n;i++)
{
a[i].x=a[i].x-minx+1;
a[i].y=a[i].y-miny+1;
}
sort(a+1,a+n+1,cmp);
memset(ans,0x7f,sizeof ans);
solve(1,n);
for(int i=1;i<=n;i++)
final[a[i].id]=a[ans[i]].id;
for(int i=1;i<=n;i++)
printf("%d ",final[i]);
printf("\n");
return 0;
}

【LOJ2586】【APIO2018】选圆圈 CDQ分治 扫描线 平衡树的更多相关文章

  1. LOJ2586 APIO2018 选圆圈

    考前挣扎 KD树好题! 暴力模拟 通过kd树的结构把子树内的圈圈框起来 然后排个序根据圆心距 <= R1+R2来判断是否有交点 然后随便转个角度就可以保持优越的nlgn啦 卡精度差评 必须写ep ...

  2. 「APIO2018选圆圈」

    「APIO2018选圆圈」 题目描述 在平面上,有 \(n\) 个圆,记为 \(c_1, c_2, \ldots, c_n\) .我们尝试对这些圆运行这个算法: 找到这些圆中半径最大的.如果有多个半径 ...

  3. BZOJ 1492 货币兑换 cdq分治或平衡树维护凸包

    题意:链接 方法:cdq分治或平衡树维护凸包 解析: 这道题我拒绝写平衡树的题解,我仅仅想说splay不要写挂,insert边界条件不要忘.del点的时候不要脑抽d错.有想写平衡树的去看140142或 ...

  4. 【BZOJ4285】使者 cdq分治+扫描线+树状数组

    [BZOJ4285]使者 Description 公元 8192 年,人类进入星际大航海时代.在不懈的努力之下,人类占领了宇宙中的 n 个行星,并在这些行星之间修建了 n - 1 条星际航道,使得任意 ...

  5. 【BZOJ1492】【Luogu P4027】 [NOI2007]货币兑换 CDQ分治,平衡树,动态凸包

    斜率在转移顺序下不满足单调性的斜率优化\(DP\),用动态凸包来维护.送命题. 简化版题意:每次在凸包上插入一个点,以及求一条斜率为\(K\)的直线与当前凸包的交点.思路简单实现困难. \(P.s\) ...

  6. BZOJ5465 APIO2018选圆圈(KD-Tree+堆)

    考虑乱搞,用矩形框圆放KD-Tree上,如果当前删除的圆和矩形有交就递归下去删.为防止被卡,将坐标系旋转一定角度即可.注意eps稍微设大一点,最好开上long double. #include< ...

  7. [BZOJ5465][APIO2018]选圆圈(KD-Tree)

    题意:给你n个圆,每次选择半径最大的,将它和与它相交的圆全部删去,输出每个圆是在哪次被删的. KD树模板题.用一个矩形框住这个圆,就可以直接剪枝了.为了防止被卡可以将点旋转一个角度,为了保险还可以多转 ...

  8. [BZOJ1492][NOI2007]货币兑换Cash(斜率优化+CDQ分治)

    1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 5838  Solved: 2345[Submit][Sta ...

  9. 【APIO2018】选圆圈(平面分块 | CDQ分治 | KDT)

    Description 给定平面上的 \(n\) 个圆,用三个参数 \((x, y, R)\) 表示圆心坐标和半径. 每次选取最大的一个尚未被删除的圆删除,并同时删除所有与其相切或相交的圆. 最后输出 ...

随机推荐

  1. arcgis api 3.x for js 共享干货系列之一自写算法实现地图量算工具(附源码下载)

    0.内容概览 Geometry 地图服务方式实现地图距离以及面积的量算,简单描述 arcgis api 提供的接口类 geometryEngine 实现地图距离以及面积的量算,简单描述 自定义距离以及 ...

  2. 安卓开发:UI组件-RadioButton和复选框CheckBox

    2.5RadioButton 为用户提供由两个及以上互斥的选项组成的选项集. 2.5.1精简代码 在按钮变多之后,多次重复书写点击事件有些繁琐,我们在这里创建一个事件OnClick,每次点击时调用该事 ...

  3. Python HTML解析器BeautifulSoup(爬虫解析器)

    BeautifulSoup简介 我们知道,Python拥有出色的内置HTML解析器模块——HTMLParser,然而还有一个功能更为强大的HTML或XML解析工具——BeautifulSoup(美味的 ...

  4. Delphi连接MySql(待测试验证,使用mysql.pas未通过)

    要在一个Delphi程序中调用Mysql数据库,查到有个资料如下,待验证,验证后会给出结果.暂时做个标记 已经验证,验证日期:2018.6.18 验证结果:不可行 验证工具:XE7,mysql5.5. ...

  5. LeetCode算法题-To Lower Case(Java实现)

    这是悦乐书的第301次更新,第320篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第169题(顺位题号是709).实现具有字符串参数str的函数ToLowerCase() ...

  6. Django REST framework基础:视图和路由

    DRF中的Request 在Django REST Framework中内置的Request类扩展了Django中的Request类,实现了很多方便的功能--如请求数据解析和认证等. 比如,区别于Dj ...

  7. .NET CORE学习笔记系列(6)——KestrelServer

    原文:http://www.cnblogs.com/artech/p/KestrelServer.html 跨平台是ASP.NET Core一个显著的特性,而KestrelServer是目前微软推出了 ...

  8. idea spring boot

    1 如何使用IntelliJ IDEA 配置Maven https://blog.csdn.net/westos_linux/article/details/78968012 2.Maven将中央仓库 ...

  9. nginx 反向代理 负载均衡

    nginx反向代理 用户(浏览器) 请求网站资源 -> 直接定位到django后台(所有的请求压力,都直接给了后台) django默认对并发性 很差,并且处理网页的静态资源,效率很差 10万个并 ...

  10. 21 python 初学(json pickle shelve)

    json: # _author: lily # _date: 2019/1/19 import json my_dict = {'name': 'lily', 'age': 18} f = open( ...