链接:Gym101630 - A Archery Tournament
题意:
共 个操作:
操作1:放置一个圆形靶子,中心在 ,其中 且靶子一定相切于 轴(即半径 ),不同靶子之间不会有重叠(可能相切);
操作2:投掷一个飞镖至 ,若击中靶子(不包含边界),则输出其编号,并删除该靶子,否则输出“ ”。
分析:
由于圆形靶子一定相切于 轴,且不会有重叠,则在直线 上,至多有 个靶子;
首先,将靶子的左右边界 、 ,飞镖的横坐标 均离散化,但是如果对于每个 都存入 上的靶子,空间复杂度可以接受,但是每次存入/删除都需改变 个,时间复杂度会达到 ;
考虑利用线段树(主要是线段树的思想),把 每个靶子的左右边界 依照线段树划分为 个区间,存入/删除靶子(线段树的每个结点为set/vector),时间复杂度 ;
查找时,对于 飞镖的横坐标 ,从根结点一路查找至叶子结点 ( ),对于每个结点,至多有 个靶子要查找,故时间复杂度为
以下代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=2e6+10;
int n,m,b[maxn];
struct operation
{
int op;
int x,y;
int L,R; //靶子的左右边界
int X; //飞镖的横坐标
}a[maxn];
void pre_work()
{
sort(b+1,b+m+1);
m=unique(b+1,b+m+1)-(b+1);
for(int i=1;i<=n;i++)
{
if(a[i].op==1)
{
a[i].L=lower_bound(b+1,b+m+1,a[i].L)-b;
a[i].R=lower_bound(b+1,b+m+1,a[i].R)-b;
}
else
a[i].X=lower_bound(b+1,b+m+1,a[i].X)-b;
}
}
set<int> s[maxn];
void updata(int rt,int l,int r,int ql,int qr,int op,int tg)
{
if(ql<=l&&r<=qr)
{
if(op==1)
s[rt].insert(tg);
else
s[rt].erase(tg);
return;
}
int mid=(l+r)>>1;
if(ql<=mid)
updata(rt<<1,l,mid,ql,qr,op,tg);
if(qr>mid)
updata(rt<<1|1,mid+1,r,ql,qr,op,tg);
}
bool query(int rt,int l,int r,int pos,int id,int &tg)
{
for(auto it:s[rt])
{
LL x=a[id].x,y=a[id].y,u=a[it].x,v=a[it].y;
if((u-x)*(u-x)+(v-y)*(v-y)<v*v)
{
tg=it;
return true;
}
}
if(l==r)
return false;
int mid=(l+r)>>1;
if(pos<=mid)
return query(rt<<1,l,mid,pos,id,tg);
else
return query(rt<<1|1,mid+1,r,pos,id,tg);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d %d %d",&a[i].op,&a[i].x,&a[i].y);
if(a[i].op==1)
{
b[++m]=a[i].L=a[i].x-a[i].y;
b[++m]=a[i].R=a[i].x+a[i].y;
}
else
b[++m]=a[i].X=a[i].x;
}
pre_work();
for(int i=1;i<=n;i++)
{
if(a[i].op==1)
updata(1,1,m,a[i].L,a[i].R,1,i);
else
{
int tg;
if(query(1,1,m,a[i].X,i,tg))
{
printf("%d\n",tg);
updata(1,1,m,a[tg].L,a[tg].R,-1,tg);
}
else
printf("-1\n");
}
}
return 0;
}