题意:给定两种操作,一种是放靶子,一种是打靶子,打中则输入序号,没打中输出-1,数据范围±1e9
暴力不可过 操作数1~2e5 n^2解法显然
一些限制,靶子与x轴相切,圆形,坐标是整数,y=R(靶子的半径)
参考了同学的解法:在线做,但是读入的时候给靶子分类,
(1<<j) <=R < (1<<(j+1))
这样遍历每组是logn,每组查的时候是logn 打靶的时候总的复杂度为 O(logn方)
怎么查?
s[j].lower_bound(mp(x,0))
查找靶子中x坐标大于等于给定的x的点
如果打在了圆心的左边Ok,这没问题
所以这样够了吗,不够,还有可能打在圆心的右边啊,这时候迭代器指向什么呢?
没错,本组的疑似圆的下一个,那我们把迭代器后退一次,再次判断一下,这个问题就完美解决了
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXLOG 30
#define mp(a,b) make_pair(a,b)
set<pair<int,int> >s[40];
set<pair<int,int> >::iterator It;
struct point{
ll x,y;
}p[202000];
bool check(ll x,ll y){
point c=p[It->second];
if(1ll*(x-c.x)*(x-c.x)+1ll*(y-c.y)*(y-c.y)<1ll*c.y*c.y){
cout<<It->second<<endl;
return true;
}
return false;
}
void solve(ll x,ll y){
for(int j=0;j<MAXLOG;j++){
It=s[j].lower_bound(mp(x,0));//找到第一个圆心x坐标>=x的点 O(log(n))
if(It!=s[j].end()&&check(x,y)){//找到了并且符合条件
s[j].erase(*It);//删点
return;
}
//如果不成功就找前一个
if(It!=s[j].begin()){//特判不是第一个点
--It;//移动到前一个
if(check(x,y)){
s[j].erase(*It);
return;
}
}
}
puts("-1");
}
inline ll read(){
ll x;
scanf("%I64d",&x);
return x;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
ll t,x,y;
t=read();
x=read();
y=read();
p[i].x=x;
p[i].y=y;
if(t==1){
for(int j=0;j<MAXLOG;j++){
if((1<<j)<=y&&y<(1<<(j+1))){
s[j].insert(mp(x,i));
}
}
}
else if(t==2){
solve(x,y);
}
}
return 0;
}