题目链接
http://codeforces.com/gym/101630/attachments/download/6401/20172018-acmicpc-northeastern-european-regional-contest-neerc-17-en.pdf
题意
给出一些圆,这些圆圆心在x轴上方,且与x轴相切,任意时刻,不存在图上的圆相交,给出两种操作:增加一个圆;向图中发送一颗子弹,如果击中某个圆,输出该圆编号并且删掉这个圆。如果未击中,则输出-1。
题解
圆与x轴相切,且圆之间不相交,这说明,如果先把圆抽象成平行于x轴的线段的话,那么包含子弹横坐标的线段不超过log(N)log(N)个,这是一个很关键的条件。
如果我们能找出这些线段,那么我们就可以进行包含判定了。
下面关键的问题在于如何找出这些线段。
这里有一个骚操作,那就是使用线段树的方式。
我们先把圆一切两半,分别考虑,先考虑子弹打在右半边的情况,左半边的情况类似。
对于圆的右半边表示成线段就是[xr,xr+r][xr,xr+r],因为圆心不会重合,所以所有的xrxr都不相同,这也正是我们要把圆切开的原因。
将线段存在线段树里,线段树xrxr位置存放xr+rxr+r,代表一个线段。并且要求线段树支持区间查询最大值的操作。
下面展示如何定位到所有的包含子弹横坐标xbxb的线段。
我们采用分治的方法,初始待考虑区间为[0,xb][0,xb](这样保证了线段的左端点<xb<xb<script type="math/tex" id="MathJax-Element-917">xbxb(相当于判断线段的右端点>xb>xb),如果大于,说明在这个区间内有满足的线段,将区间进一步细分。
直到区间细分到长度为11<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;
}