题目:https://loj.ac/problem/2586

只会 19 分的暴力。

y 都相等,仍然按直径从大到小做。如果当前圆没有被删除,那么用线段树把 [ x-r , x+r ] 都打上它的标记。

看当前圆有没有被删除,只要看 x-r 和 x+r 两个位置上的标记就行了。因为被删除的话当前圆的直径更小,有相交的话, x-r 或 x+r 一定在对方内部。可以 x-r 和 x+r 分别在两个圆内部,看看哪个更大即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define ls Ls[cr]
#define rs Rs[cr]
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
ll Sqr(int x){return (ll)x*x;}
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
const int N=3e5+;
int n,ans[N];
struct Node{
int x,y,r,id;
}a[N];
namespace S1{
bool cmp(Node u,Node v)
{return u.r==v.r?u.id<v.id:u.r>v.r;}
bool chk(Node u,Node v)
{ return Sqr(u.r+v.r)>=Sqr(u.x-v.x)+Sqr(u.y-v.y);}
void solve()
{
sort(a+,a+n+,cmp);
for(int i=;i<=n;i++)
{
int cr=a[i].id; if(ans[cr])continue; ans[cr]=cr;
for(int j=i+;j<=n;j++)
if(chk(a[i],a[j])&&!ans[a[j].id])ans[a[j].id]=cr;//!ans[]!!!
}
for(int i=;i<=n;i++)printf("%d ",ans[i]);puts("");
}
}
namespace S2{
const int M=N<<;
int dy[N],tp[N<<],m,c0[N],c1[N],tot,Ls[M],Rs[M],tg[M];
bool cmp(Node u,Node v){return u.r==v.r?u.id<v.id:u.r>v.r;}
void build(int l,int r,int cr)
{
if(l==r)return; int mid=l+r>>;
ls=++tot; build(l,mid,ls);
rs=++tot; build(mid+,r,rs);
}
int qry(int l,int r,int cr,int p)
{
if(l==r)return tg[cr]; int mid=l+r>>;
if(p<=mid)return tg[cr]|qry(l,mid,ls,p);
return tg[cr]|qry(mid+,r,rs,p);
}
void mdfy(int l,int r,int cr,int L,int R,int k)
{
if(l>=L&&r<=R){tg[cr]=k;return;}
int mid=l+r>>;
if(L<=mid)mdfy(l,mid,ls,L,R,k);
if(mid<R)mdfy(mid+,r,rs,L,R,k);
}
void solve()
{
sort(a+,a+n+,cmp);
for(int i=;i<=n;i++)dy[i]=a[i].id;
for(int i=;i<=n;i++)
{tp[++m]=a[i].x-a[i].r; tp[++m]=a[i].x+a[i].r;}
sort(tp+,tp+m+); m=unique(tp+,tp+m+)-tp-;
for(int i=;i<=n;i++)
{
c0[i]=lower_bound(tp+,tp+m+,a[i].x-a[i].r)-tp;
c1[i]=lower_bound(tp+,tp+m+,a[i].x+a[i].r)-tp;
}
tot=; build(,m,);
for(int i=;i<=n;i++)
{
int d0=qry(,m,,c0[i]), d1=qry(,m,,c1[i]), cr=a[i].id;
if(d0&&d1){ans[cr]=dy[Mn(d0,d1)];continue;}
if(d0||d1){ans[cr]=dy[d0|d1];continue;}
ans[cr]=cr; mdfy(,m,,c0[i],c1[i],i);
}
for(int i=;i<=n;i++)printf("%d ",ans[i]);puts("");
}
}
int main()
{
n=rdn();
for(int i=;i<=n;i++)
a[i].x=rdn(),a[i].y=rdn(),a[i].r=rdn(),a[i].id=i;
if(n<=){S1::solve();return ;}
bool fg=;
for(int i=;i<=n;i++)if(a[i].y){fg=;break;}
if(!fg){S2::solve();return ;}
return ;
}

正解竟然是 KD 树。

用矩形来表示一个圆,把 KD 树建出来。在删除一个圆的时候在 KD 树上找和它相交的圆。矩形就是估价。

可以把坐标轴旋转一定角度。自己旋转了 60 度。那么原来的 x 和 y 算一下的话会变成 \( \frac{x}{cos\alpha} + ( y - \frac{x}{cos\alpha} * sin\alpha )sin\alpha , ( y - \frac{x}{cos\alpha}*sin\alpha )cos\alpha ) \) 。

eps 设成 1e-8 会 WA , 1e-3 就行了。也不知何故。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ls c[cr][0]
#define rs c[cr][1]
#define db double
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
db Mn(db a,db b){return a<b?a:b;}
db Mx(db a,db b){return a>b?a:b;}
db Sqr(db x){return x*x;}
const int N=3e5+;
const db eps=1e-,pi=acos(-),nc=cos(pi/),ns=sin(pi/);
int dcmp(db x){ if(x<-eps)return -;if(x>eps)return ;return ;} int n,tot,c[N][],ans[N]; bool fx;
struct Dt{ db x[];int r,id;}a[N];
struct Node{
db x[],y[];Dt a;
}p[N];
bool cmp(Dt u,Dt v){return u.x[fx]<v.x[fx];}
bool cmp2(Dt u,Dt v){return u.r==v.r?u.id<v.id:u.r>v.r;}
void pshp(int cr)
{
for(int i=;i<=;i++)
{p[cr].x[i]=p[cr].a.x[i]-p[cr].a.r;
p[cr].y[i]=p[cr].a.x[i]+p[cr].a.r;}
if(ls)
{
for(int i=;i<=;i++)
{p[cr].x[i]=Mn(p[cr].x[i],p[ls].x[i]);
p[cr].y[i]=Mx(p[cr].y[i],p[ls].y[i]);}
}
if(rs)
{
for(int i=;i<=;i++)
{p[cr].x[i]=Mn(p[cr].x[i],p[rs].x[i]);
p[cr].y[i]=Mx(p[cr].y[i],p[rs].y[i]);}
}
}
void build(int l,int r,int cr,bool nw)
{
int mid=l+r>>; fx=nw;nth_element(a+l,a+mid,a+r+,cmp);
p[cr].a=a[mid];
if(l<mid){ ls=++tot;build(l,mid-,ls,!nw);}
if(mid<r){ rs=++tot;build(mid+,r,rs,!nw);}
pshp(cr);
}
bool Out(int cr,db x[],int r)
{
for(int i=;i<=;i++)
{
if(dcmp(p[cr].x[i]-(x[i]+r))>)return true;
if(dcmp((x[i]-r)-p[cr].y[i])>)return true;
}
return false;
}
bool itr(Dt u,Dt v)
{return dcmp(Sqr(u.r+v.r)-Sqr(u.x[]-v.x[])-Sqr(u.x[]-v.x[]))>=;}
void mdfy(int cr,Dt k)
{
if(Out(cr,k.x,k.r))return;
if(!ans[p[cr].a.id]&&itr(p[cr].a,k))ans[p[cr].a.id]=k.id;
if(ls)mdfy(ls,k); if(rs)mdfy(rs,k);
}
int main()
{
n=rdn();
for(int i=,x,y;i<=n;i++)
{
x=rdn(); y=rdn(); a[i].r=rdn();a[i].id=i;
db t0=x/nc, t1=y-x/nc*ns;
a[i].x[]=t0+t1*ns; a[i].x[]=t1*nc;
}
tot=;build(,n,,); sort(a+,a+n+,cmp2);
for(int i=;i<=n;i++)
{
if(ans[a[i].id])continue;
mdfy(,a[i]);
}
for(int i=;i<=n;i++)printf("%d ",ans[i]);puts("");
return ;
}

LOJ 2586 「APIO2018」选圆圈——KD树的更多相关文章

  1. 【LOJ】#2586. 「APIO2018」选圆圈

    题解 不旋转坐标系,TLE,旋转坐标系,最慢一个点0.5s--maya,出题人数据水平很高了-- 好吧,如果你不旋转坐标系,写一个正确性和复杂度未知的K - D树,没有优化,你可以得到87分的好成绩 ...

  2. 「APIO2018」选圆圈

    传送门 Description 有\(n\)个圆,每次找到这些圆中半径最大中的编号最小的圆,删除ta及与其有交集的所有圆. 对于每个圆,求出它是被哪一个圆删除的. Solution  K-D Tree ...

  3. LOJ 2585 「APIO2018」新家 ——线段树分治+二分答案

    题目:https://loj.ac/problem/2585 算答案的时候要二分! 这样的话,就是对于询问位置 x ,二分出一个最小的 mid 使得 [ x-mid , x+mid ] 里包含所有种类 ...

  4. LOJ #2585. 「APIO2018」新家

    #2585. 「APIO2018」新家 https://loj.ac/problem/2585 分析: 线段树+二分. 首先看怎样数颜色,正常的时候,离线扫一遍右端点,每次只记录最右边的点,然后查询左 ...

  5. 「NOI2019」弹跳(KD树)

    题意:w×h网格中有n个点,m条边.每条边可以从p点花费t时间到一个矩形中的任意点,求1号点到每个点的最少时间. \(1<=w,h<=n<=70000,1<=m<=150 ...

  6. LOJ #2587「APIO2018」铁人两项

    是不是$ vector$存图非常慢啊...... 题意:求数对$(x,y,z)$的数量使得存在一条$x$到$z$的路径上经过$y$,要求$x,y,z$两两不同  LOJ #2587 $ Solutio ...

  7. LOJ 2587 「APIO2018」铁人两项——圆方树

    题目:https://loj.ac/problem/2587 先写了 47 分暴力. 对于 n<=50 的部分, n3 枚举三个点,把图的圆方树建出来,合法条件是 c 是 s -> f 路 ...

  8. 【刷题】LOJ 2587 「APIO2018」铁人两项

    题目描述 比特镇的路网由 \(m\) 条双向道路连接的 \(n\) 个交叉路口组成. 最近,比特镇获得了一场铁人两项锦标赛的主办权.这场比赛共有两段赛程:选手先完成一段长跑赛程,然后骑自行车完成第二段 ...

  9. LOJ #2719. 「NOI2018」冒泡排序(组合数 + 树状数组)

    题意 给你一个长为 \(n\) 的排列 \(p\) ,问你有多少个等长的排列满足 字典序比 \(p\) 大 : 它进行冒泡排序所需要交换的次数可以取到下界,也就是令第 \(i\) 个数为 \(a_i\ ...

随机推荐

  1. MFC 添加背景图片并让控件背景透明

    /*添加背景图片*/ BOOL CTOOLDlg::OnEraseBkgnd(CDC* pDC) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CDialog::OnEraseBkg ...

  2. 多路复用 阻塞/非阻塞IO模型 网络IO两个阶段

    1.网络IO的两个阶段 waitdata copydata send 先经历:copydata阶段 recv 先经历:waitdata阶段 再经历 copydata阶段 2.阻塞的IO模型 之前写的都 ...

  3. Access denied for user 'root'@'IP' (using password:YES)解决方法

    在MySql的使用过程中,碰到“Access denied for user 'root'@'IP' (using password:YES)”的问题,使用以下语句修改后还是不行. GRANT ALL ...

  4. SQL注入之Sqli-labs系列第十五关和第十六关(基于POST的时间盲注)

    开始挑战第十五关(Blind- Boolian Based- String)和 第十六关(Blind- Time Based- Double quotes- String) 访问地址,输入报错语句 ' ...

  5. Linux shell脚本中shift

    Linux shell脚本中shift的用法说明 shift命令用于对参数的移动(左移),通常用于在不知道传入参数个数的情况下依次遍历每个参数然后进行相应处理(常见于Linux中各种程序的启动脚本). ...

  6. 数据库和redis的一致性

    之前的讲解,主要是在讲解redis如何支撑海量数据.高并发读写.高可用服务的架构 从这一讲开始,正式开始做业务系统的开发 商品详情页,缓存架构,90%是大量的业务(没有什么级数含量),10%最有级数含 ...

  7. JavaScript BOM-11-BOM的核心-window对象; window对象的控制,弹出窗口方法; 超时调用; 间歇调用; location对象常用属性; 位置操作--location.reaplace,location.reload(); BOM中的history对象; Screen对象及其常用属性; Navigator对象;

    JavaScript BOM 学习目标 1.掌握什么是BOM 2.掌握BOM的核心-window对象 3.掌握window对象的控制.弹出窗口方法 什么是bom BOM(browser object ...

  8. threejs 空间位置转为屏幕像素xy位置

    function createVector(x, y, z, camera, width, height) { var p = new THREE.Vector3(x, y, z); var vect ...

  9. YII2.0使用ActiveForm表单(转)

    Controller控制器层代码 <?php namespace frontend\controllers; use frontend\models\UserForm; class UserCo ...

  10. Django之模型层-单表操作

    单表操作 添加记录 方式1 # 先实例化models中的对象,按照定义的语句规则传入参数,然后使用对象调用save()保存到数据库 book_obj = Book(id=1,title='python ...