【洛谷】 [APIO2018] 选圆圈 -KD-tree

传送门:luogu4631


题解

KD-tree乱搞剪枝就过了?

把每个圆看成 ( x − r , y − r , x + r , y + r ) (x-r,y-r,x+r,y+r) (xr,yr,x+r,y+r)的矩阵,KD-tree维护所有未标号圆,暴力判相交即可。

最好一开始瞎旋一下坐标

p.s
eps设成1e-6会WA,改成1e-3才能A?


代码

#include<bits/stdc++.h>
#define fi first
#define sc second
#define pb push_back
//#define ccosi
using namespace std;
const int N=3e5+10;
typedef long long ll;
typedef double db;
const db inf=1e233,eps=1e-3;

int n,col[N],rt,D;
db ag,sum,avr,vl[2];

struct P{db d[2];int r,id;}p[N],cur;
struct node{
	int ch[2];P pt;
	db mn[2],mx[2];
}t[N];

inline bool cmp(P a,P b){return a.d[D]<b.d[D];}
inline bool cmpr(P a,P b){return (a.r==b.r)?a.id<b.id:a.r>b.r;}

#ifndef ccosi
char buf[(1<<15)];int p1=0,p2=0;
inline char gc()
{
  if(p1==p2) p1=0,p2=fread(buf,1,(1<<15),stdin);
  return (p1==p2)?EOF:buf[p1++];
}
#endif

#ifdef ccosi
#define gc getchar
#endif

char cp;
template<class T>inline void rd(T &x)
{
	cp=gc();x=0;int f=0;
	for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
	if(f) x=-x;
}

inline void prit(int x)
{if(x>9) prit(x/10);putchar('0'+(x%10));}

inline db sqr(db x){return x*x;}

inline void pu(int x)
{
	int i,l=t[x].ch[0],r=t[x].ch[1];
	for(int i=0;i<2;++i){
		if(col[t[x].pt.id]) t[x].mn[i]=inf,t[x].mx[i]=-inf;
		else t[x].mn[i]=t[x].pt.d[i]-t[x].pt.r,t[x].mx[i]=t[x].pt.d[i]+t[x].pt.r;
		if(l) t[x].mn[i]=min(t[x].mn[i],t[l].mn[i]),t[x].mx[i]=max(t[x].mx[i],t[l].mx[i]);
		if(r) t[x].mn[i]=min(t[x].mn[i],t[r].mn[i]),t[x].mx[i]=max(t[x].mx[i],t[r].mx[i]);
	}
}

int build(int l,int r)
{
	if(l>r) return 0;
	int i,j,mid=(l+r)>>1;
	for(j=0;j<2;++j){
		for(sum=0,i=l;i<=r;++i) sum+=p[i].d[j];
		avr=sum/(r-l+1);sum=0;
		for(i=l;i<=r;++i) sum+=sqr(p[i].d[j]-avr);
		vl[j]=sum;
	}
	D=(vl[1]>vl[0])?1:0;nth_element(p+l,p+mid,p+r+1,cmp);
	t[mid].pt=p[mid];
	t[mid].ch[0]=build(l,mid-1);t[mid].ch[1]=build(mid+1,r);
	pu(mid);return mid;
}

inline bool ot(int x)
{
	return (t[x].mn[0]-eps>cur.d[0]+cur.r || t[x].mn[1]-eps>cur.d[1]+cur.r || 
	t[x].mx[0]+eps<cur.d[0]-cur.r || t[x].mx[1]+eps<cur.d[1]-cur.r);
}

inline bool inn(P a)
{return (sqr(a.d[0]-cur.d[0])+sqr(a.d[1]-cur.d[1])<sqr(a.r+cur.r)+eps);}

void fd(int x)
{
	if(ot(x)) return;
	if((!col[t[x].pt.id]) && inn(t[x].pt)) col[t[x].pt.id]=cur.id;
	if(t[x].ch[0]) fd(t[x].ch[0]);
	if(t[x].ch[1]) fd(t[x].ch[1]);
	pu(x);
}

int main(){
	srand(19260817);ag=rand()/(db)RAND_MAX; 
	int i,j,x,y,z;db cs=cos(ag),sn=sin(ag);
    rd(n);
    for(i=1;i<=n;++i) {
    	rd(x);rd(y);rd(z);
		p[i].d[0]=cs*x+sn*y;p[i].d[1]=cs*y-sn*x;
		p[i].r=z;p[i].id=i;
	}
	rt=build(1,n);sort(p+1,p+n+1,cmpr);
	for(i=1;i<=n;++i) if(!col[p[i].id]){
		cur=p[i];col[cur.id]=cur.id;fd(rt);
	}
	for(i=1;i<=n;++i) {prit(col[i]);if(i<n) putchar(' ');}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值