题目链接
题意
给出一些圆,这些圆圆心在x轴上方,且与x轴相切,任意时刻,不存在图上的圆相交,给出两种操作:增加一个圆;向图中发送一颗子弹,如果击中某个圆,输出该圆编号并且删掉这个圆。如果未击中,则输出-1。
题解
圆与x轴相切,且圆之间不相交,这说明,如果先把圆抽象成平行于x轴的线段的话,那么包含子弹横坐标的线段不超过
log(N)
l
o
g
(
N
)
个,这是一个很关键的条件。
如果我们能找出这些线段,那么我们就可以进行包含判定了。
下面关键的问题在于如何找出这些线段。
这里有一个骚操作,那就是使用线段树的方式。
我们先把圆一切两半,分别考虑,先考虑子弹打在右半边的情况,左半边的情况类似。
对于圆的右半边表示成线段就是
[xr,xr+r]
[
x
r
,
x
r
+
r
]
,因为圆心不会重合,所以所有的
xr
x
r
都不相同,这也正是我们要把圆切开的原因。
将线段存在线段树里,线段树
xr
x
r
位置存放
xr+r
x
r
+
r
,代表一个线段。并且要求线段树支持区间查询最大值的操作。
下面展示如何定位到所有的包含子弹横坐标
xb
x
b
的线段。
我们采用分治的方法,初始待考虑区间为
[0,xb]
[
0
,
x
b
]
(这样保证了线段的左端点
<xb
<
x
b
<script type="math/tex" id="MathJax-Element-917">
xb
x
b
(相当于判断线段的右端点
>xb
>
x
b
),如果大于,说明在这个区间内有满足的线段,将区间进一步细分。
直到区间细分到长度为
1
1
<script type="math/tex" id="MathJax-Element-920">1</script>,停止,这就是一个满足条件的线段。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
const int maxn = 5e5+7;
int n;
int tp,x,y,len;
int segmx[maxn<<2],segmi[maxn<<2];
int al[maxn],cat = 0;
int mp[maxn];
#define pr(x) cout<<#x<<":"<<x<<endl
inline int getid(int x){
return lower_bound(al,al+len,x) - al;
}
void updatemx(int rt,int l,int r,int pos,int x){
if(l == r){
segmx[rt] = x;
return ;
}
int mid = (l+r)/2;
if(pos <= mid) updatemx(rt*2,l,mid,pos,x);
else updatemx(rt*2+1,mid+1,r,pos,x);
segmx[rt] = max(segmx[rt*2],segmx[rt*2+1]);
}
void updatemi(int rt,int l,int r,int pos,int x){
if(l == r){
segmi[rt] = x;
return ;
}
int mid = (l+r)/2;
if(pos <= mid) updatemi(rt*2,l,mid,pos,x);
else updatemi(rt*2+1,mid+1,r,pos,x);
segmi[rt] = min(segmi[rt*2],segmi[rt*2+1]);
}
int querymx(int rt,int l,int r,int ul,int ur){
if(ul <= l && r <= ur)
return segmx[rt];
if(r < ul || l > ur) return -2e9;
int mid = (l+r)/2;
int ansl = querymx(rt*2,l,mid,ul,ur);
int ansr = querymx(rt*2+1,mid+1,r,ul,ur);
return max(ansl,ansr);
}
int querymi(int rt,int l,int r,int ul,int ur){
if(ul <= l && r <= ur)
return segmi[rt];
if(r < ul || l > ur) return 2e9;
int mid = (l+r)/2;
int ansl = querymi(rt*2,l,mid,ul,ur);
int ansr = querymi(rt*2+1,mid+1,r,ul,ur);
return min(ansl,ansr);
}
int tps[maxn],xs[maxn],ys[maxn];
int ans[maxn];
bool check(long long x,long long y,int id){
long long dx = x - xs[id];
long long dy = y - ys[id];
if(dx*dx + dy*dy < y*y) return 1;
return 0;
}
void dcmx(int l,int r,int id){
int y = querymx(1,0,len-1,l,r);
if(y <= xs[id]) return ;
if(l == r){
int x = al[l];
y = y - x;
if(check(x,y,id)) {
ans[id] = mp[l];
mp[l] = 0;
updatemx(1,0,len-1,l,-2e9);
updatemi(1,0,len-1,l,2e9);
}
return ;
}
int mid = (l+r)/2;
dcmx(l,mid,id);
dcmx(mid+1,r,id);
}
void dcmi(int l,int r,int id){
int y = querymi(1,0,len-1,l,r);
if(y >= xs[id]) return ;
if(l == r){
int x = al[l];
y = -y + x;
if(check(x,y,id)) {
ans[id] = mp[l];
mp[l] = 0;
updatemi(1,0,len-1,l,2e9);
updatemx(1,0,len-1,l,-2e9);
}
return ;
}
int mid = (l+r)/2;
dcmi(l,mid,id);
dcmi(mid+1,r,id);
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;++i){
scanf("%d%d%d",&tp,&x,&y);
tps[i] = tp,xs[i] = x,ys[i] = y;
if(tp == 1){
al[cat++] = x;
al[cat++] = x-y;
al[cat++] = x+y;
}
else{
al[cat++] = x;
}
}
sort(al,al+cat);
len = unique(al,al+cat) - al;
memset(mp,0,sizeof(mp));
for(int i = 0;i < maxn<<2;++i) segmx[i] = -2e9,segmi[i] = 2e9;
for(int i = 1;i <= n;++i){
int idx = getid(xs[i]);
if(tps[i] == 1){
mp[idx] = i;
updatemx(1,0,len-1,getid(xs[i]),xs[i]+ys[i]);
updatemi(1,0,len-1,getid(xs[i]),xs[i]-ys[i]);
}
else{
dcmx(0,idx,i);
dcmi(idx,len-1,i);
}
}
for(int i = 1;i <= n;++i){
if(tps[i] == 2){
printf("%d\n",ans[i]?ans[i]:-1);
}
}
return 0;
}