「APIO2018选圆圈」
「APIO2018选圆圈」
题目描述
在平面上,有 \(n\) 个圆,记为 \(c_1, c_2, \ldots, c_n\) 。我们尝试对这些圆运行这个算法:
- 找到这些圆中半径最大的。如果有多个半径最大的圆,选择编号最小的。记为 \(c_i\) 。
- 删除 \(c_i\) 及与其有交集的所有圆。两个圆有交集当且仅当平面上存在一个点,这个点同时在这两个圆的圆周上或圆内。
- 重复上面两个步骤直到所有的圆都被删除。
当 \(c_i\) 被删除时,若循环中第1步选择的圆是 \(c_j\) ,我们说 \(c_i\) 被 \(c_j\) 删除。对于每个圆,求出它是被哪一个圆删除的。
解题思路 :
越抓越痒有理想,\(n^2\) 有信仰。
首先把圆心拿出来建KD树,对于每一棵子树,维护出一个矩形框住这些圆的并,每次删除爆枚整棵树通过判断和这个矩形有没有交来剪枝,最坏复杂度 \(O(n^2)\) ,好像旋转某个角度就卡不掉了。
话说写APIO题卡评测真是爽啊,顺便写篇博客存个KD树板子。
/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int f = 0, ch = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
const int N = 600005;
const double sina = sqrt(2)/2, cosa = sina, eps = 5e-2;
int ans[N], Sgn, rt, n;
inline double sqr(double x){ return x * x; }
inline void chkmax(double &x, double y){ if(y > x) x = y; }
inline void chkmin(double &x, double y){ if(y < x) x = y; }
struct Point{ double x, y, r; int id; } a[N], s[N];
inline bool cmp(Point A, Point B){
if(Sgn == 0) return A.x < B.x;
if(Sgn == 1) return A.y < B.y;
if(Sgn == 2) return A.r == B.r ? (A.id < B.id) : (A.r > B.r);
}
struct Kdtree{
int ch[N][2];
struct Node{
double mx[2], mn[2]; int id;
}T[N];
inline void update(int x){
T[x].mx[0] = a[x].x + a[x].r, T[x].mx[1] = a[x].y + a[x].r;
T[x].mn[0] = a[x].x - a[x].r, T[x].mn[1] = a[x].y - a[x].r;
for(int i = 0; i < 2; i++) if(ch[x][i])
for(int j = 0; j < 2; j++){
chkmax(T[x].mx[j], T[ch[x][i]].mx[j]);
chkmin(T[x].mn[j], T[ch[x][i]].mn[j]);
}
}
inline void build(int &u, int l, int r, int sgn){
if(l > r) return;
int mid = l + r >> 1; u = mid, Sgn = sgn;
nth_element(a + l, a + mid, a + r + 1, cmp);
build(ch[u][0], l, mid - 1, sgn ^ 1);
build(ch[u][1], mid + 1, r, sgn ^ 1), update(u);
}
inline void Delete(int u, Point now){
if(!u || now.x - now.r > T[u].mx[0] || now.x + now.r < T[u].mn[0]
|| now.y - now.r > T[u].mx[1] || now.y + now.r < T[u].mn[1]) return;
if(!ans[a[u].id]){
if(sqr(now.x-a[u].x)+sqr(now.y-a[u].y) <= sqr(now.r+a[u].r) + eps)
ans[a[u].id] = now.id, a[u].x = a[u].y = 0, a[u].r = -inf;
}
Delete(ch[u][0], now), Delete(ch[u][1], now), update(u);
}
}van;
int main(){
read(n);
for(int i = 1, x, y; i <= n; i++){
read(x), read(y), read(a[i].r), a[i].id = i;
a[i].x = (double) x * cosa - y * sina;
a[i].y = (double) x * sina + y * cosa;
}
Sgn = 2, sort(a + 1, a + n + 1, cmp);
for(int i = 1; i <= n; i++) s[i] = a[i];
van.build(rt, 1, n, 0);
for(int i = 1; i <= n; i++)
if(!ans[s[i].id]) van.Delete(rt, s[i]);
for(int i = 1; i <= n; i++) printf("%d ", ans[i]);
return 0;
}
「APIO2018选圆圈」的更多相关文章
- 「APIO2018新家」
「APIO2018新家」 题目描述 五福街是一条笔直的道路,这条道路可以看成一个数轴,街上每个建筑物的坐标都可以用一个整数来表示.小明是一位时光旅行者,他知道在这条街上,在过去现在和未来共有 \(n\ ...
- BZOJ5465 APIO2018选圆圈(KD-Tree+堆)
考虑乱搞,用矩形框圆放KD-Tree上,如果当前删除的圆和矩形有交就递归下去删.为防止被卡,将坐标系旋转一定角度即可.注意eps稍微设大一点,最好开上long double. #include< ...
- [BZOJ5465][APIO2018]选圆圈(KD-Tree)
题意:给你n个圆,每次选择半径最大的,将它和与它相交的圆全部删去,输出每个圆是在哪次被删的. KD树模板题.用一个矩形框住这个圆,就可以直接剪枝了.为了防止被卡可以将点旋转一个角度,为了保险还可以多转 ...
- LG4819/BZOJ2438 「中山市选2011」杀人游戏 Tarjan缩点+概率
问题描述 LG4819 BZOJ2438 题解 发现如果有一些人之间认识关系形成环,只需要问一个人就能把控整个环. \(\mathrm{Tarjan}\)缩点. 缩点之后所有入度为\(0\)的点,必须 ...
- LOJ2586 APIO2018 选圆圈
考前挣扎 KD树好题! 暴力模拟 通过kd树的结构把子树内的圈圈框起来 然后排个序根据圆心距 <= R1+R2来判断是否有交点 然后随便转个角度就可以保持优越的nlgn啦 卡精度差评 必须写ep ...
- 【LG4631】[APIO2018]Circle selection 选圆圈
[LG4631][APIO2018]Circle selection 选圆圈 题面 洛谷 题解 用\(kdt\)乱搞剪枝. 维护每个圆在\(x.y\)轴的坐标范围 相当于维护一个矩形的坐标范围为\([ ...
- LOJ #2585. 「APIO2018」新家
#2585. 「APIO2018」新家 https://loj.ac/problem/2585 分析: 线段树+二分. 首先看怎样数颜色,正常的时候,离线扫一遍右端点,每次只记录最右边的点,然后查询左 ...
- 【APIO2018】选圆圈(平面分块 | CDQ分治 | KDT)
Description 给定平面上的 \(n\) 个圆,用三个参数 \((x, y, R)\) 表示圆心坐标和半径. 每次选取最大的一个尚未被删除的圆删除,并同时删除所有与其相切或相交的圆. 最后输出 ...
- Libre 6007 「网络流 24 题」方格取数 / Luogu 2774 方格取数问题 (网络流,最大流)
Libre 6007 「网络流 24 题」方格取数 / Luogu 2774 方格取数问题 (网络流,最大流) Description 在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数.现要从 ...
随机推荐
- bzoj 1072 状压DP
我们用w[i][j]来表示,i是一个二进制表示我们选取了s中的某些位,j表示这些位%d为j,w[i][j]则表示这样情况下的方案数,那么我们可以得到转移.w[i|(1<<k)][(j*10 ...
- 1-编程基础及Python环境部署
目录 1 编程基础 1.1 基本概念 1.2 语言分类 1.3 高级语言的发展 2 程序 3 python的语言介绍 4 Python的解释器 5 Python版本区别 6 Python安装 6.1 ...
- Git远程操作详解【转】
转自:http://www.ruanyifeng.com/blog/2014/06/git_remote.html 作者: 阮一峰 日期: 2014年6月12日 Git是目前最流行的版本管理系统,学会 ...
- 在字符串S1中删除字符串S2中所包含的字符【转】
转自:http://www.cnblogs.com/tolimit/p/4202959.html /************************************************** ...
- HDU 6194 string string string 2017沈阳网络赛 后缀数组
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6194 题意:告诉你一个字符串和k , 求这个字符串中有多少不同的子串恰好出现了k 次. 解法:后缀数组 ...
- serialVersionUID的作用(转)
本文系转载,原文链接:http://swiftlet.net/archives/1268 serialVersionUID适用于Java的序列化机制.简单来说,Java的序列化机制是通过判断类的ser ...
- 20180830 安装git时报错,
安装:https://blog.csdn.net/u013256816/article/details/54743470 解决问题:https://blog.csdn.net/daojibruce/a ...
- java版云笔记(四)
页面的笔记本加载完成了,接下来就是点击笔记本显示将笔记显示,同时把笔记在右边的编辑器中,同时把编辑后的笔记更新. 注:这个项目的sql文件,需求文档,需要的html文件,jar包都可以去下载,下载地址 ...
- php 高并发解决方案(用于抢购) 转载
最近在做一个团购项目,遇到个问题,就是在抢购.秒杀.抽奖等活动时,库存数量有限,但是同时下单人数超过了库存数量,就会导致商品超售问题.那么我们怎么来解决这个问题呢,我的思路如下: sql1:查询商品库 ...
- leetcode 之trap water(8)
这题不太好想.可以先扫描找到最高的柱子,然后分别处理两边:记录下当前的局部最高点,如果当前点小于局部最高点,加上, 反则,替换当前点为局部最高点. int trapWater(int A[], int ...