【题解】
意思是说每个靶子离射击点的距离为z.然后描述矩形靶子的左下角和右下角。
射击m次。每次射击靶子之后。靶子会毁坏(假如同一个点有多个靶子重叠,最前面那个靶子坏掉。其他靶子还可以射击);
做法:
给每个子弹记录三个信息x,y,bianhao。即坐标以及各个子弹出现的先后顺序。
然后以靶子离射击点的距离为关键字从小到大排序。
处理第一个靶子是被哪一个子弹射击了。即在靶子所在范围内的子弹。且要求子弹的编号(射击顺序)最小。
这个问题可以用kd-tree来解决。
以子弹的点为元素建立kdtree
知道一个靶子是被哪个子弹射击之后。在kdtree中删掉这个子弹。然后继续处理下一个靶子。
编程复杂度很高。
【代码】
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 105000;
const int INF = 2100000000;
struct target
{
int mi_n[2], ma_x[2], z, n;
};
struct point
{
int min, n, dot, d[2],fa,l,r;
};
int n, m, root, now, txl, txr, tyl, tyr, ans[MAXN] = { 0 };
target rec[MAXN];
point t[MAXN];
void input_data()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d%d%d%d%d", &rec[i].mi_n[0], &rec[i].ma_x[0], &rec[i].mi_n[1], &rec[i].ma_x[1], &rec[i].z);
rec[i].n = i;
}
scanf("%d", &m);
for (int i = 1; i <= m; i++)
{
scanf("%d%d", &t[i].d[0], &t[i].d[1]);
t[i].n = i;
}
}
bool cmp_1(point a, point b)
{
return a.d[now] < b.d[now];
}
void gengxin(int father, int son)
{
if (t[father].min > t[son].min)
{
t[father].min = t[son].min;
t[father].dot = t[son].dot;
}
}
void up_data(int rt)
{
t[rt].min = t[rt].n; t[rt].dot = rt; //dot可以说是当前这个子树的编号最小的点的节点。
if (t[rt].n == 0) //如果点已经删掉了
{
t[rt].min = INF;
t[rt].dot = 0;
}
int l = t[rt].l, r = t[rt].r;
if (l)
gengxin(rt, l);
if (r)
gengxin(rt, r);
}
int build(int begin, int end, int fa,int fx)
{
int m = (begin + end) >> 1;
now = fx;
nth_element(t + begin, t + m, t + end + 1, cmp_1);
t[m].fa = fa;
if (begin < m)
t[m].l = build(begin, m - 1, m, 1 - fx);
if (m < end)
t[m].r = build(m + 1, end, m, 1 - fx);
up_data(m);
return m;
}
bool inrange(int x, target b,int fx)
{
return ((b.mi_n[fx] <= x) && (x <= b.ma_x[fx]));
}
int query(int rt, int fx, int r, int xl, int xr, int yl, int yr)
{//r是当前处理的靶子编号 txl,txr,tyl,tyr是靶子信息。
//xl,xr,yl,yr是当前kd-tree的节点所在子树的所有点的范围(如果不确定就写成无穷大)
if (!rt)//我们会不断缩小这个范围。
return 0;
if (!t[rt].dot)
return 0;
if (txl <= xl && xr <= txr && tyl <= yl && yr <= tyr)
return t[rt].dot;
int t1 = 0,t2 = 0;
if (inrange(t[rt].d[fx], rec[r], fx))//这个节点的fx坐标在靶子范围内
{ //则左右儿子都要递归求解。则后序还要查看当前这个节点是否能更新。
if (fx == 0) //如果是以x轴排序
{
t1 = query(t[rt].l, 1-fx, r, xl, t[rt].d[0], yl, yr);
t2 = query(t[rt].r, 1-fx, r, t[rt].d[0], xr, yl, yr);
}
else//以y轴排序
{
t1 = query(t[rt].l, 1-fx, r, xl, xr, yl, t[rt].d[1]);
t2 = query(t[rt].r, 1-fx, r, xl, xr, t[rt].d[1], yr);
}
}
else//这个靶子的xy坐标没有完全把这个点包住
{//则只要返回左或右儿子
if (t[rt].d[fx] < rec[r].mi_n[fx])//大于这个节点的fx坐标 等号的情况上面已经考虑了
{
if (fx == 0)
return query(t[rt].r, 1-fx, r, t[rt].d[0], xr, yl, yr);
else
return query(t[rt].r, 1-fx, r, xl, xr, t[rt].d[1], yr);
}
if (rec[r].ma_x[fx] < t[rt].d[fx])//小于这个节点的fx坐标
{
if (fx == 0)
return query(t[rt].l, 1-fx, r, xl, t[rt].d[0], yl, yr);
else
return query(t[rt].l, 1-fx, r, xl, xr, yl, t[rt].d[1]);
}
}
int fi = 0; int temp = INF;
if (t1!=0)
if (t[t1].n < temp)//千万不要写成t[t1].min < temp
{//因为可能t[t1].min对应的点不在我们所需的范围内
//这个rt就已经返回的是所需的节点的编号了,不要乱来
temp = t[t1].n;
fi = t1;
}
if (t2 != 0)
if (t[t2].n < temp)
{
temp = t[t2].n;
fi = t2;
}
if (t[rt].n != 0 && t[rt].n < temp)//这是尝试用当前这个节点来更新。
if (inrange(t[rt].d[0],rec[r],0) && inrange(t[rt].d[1],rec[r],1))
{
temp = t[rt].n;
fi = rt;
}
return fi;
}
void adjust(int rt) //删掉一个点后调整相关点的信息
{
up_data(rt);
if (rt != root)
adjust(t[rt].fa);
}
bool cmp_2(target a, target b)
{
return a.z < b.z;
}
void get_ans()
{
root = build(1, m, 0, 0);
sort(rec + 1, rec + 1 + n, cmp_2);
for (int i = 1; i <= n; i++)
{
txl = rec[i].mi_n[0], txr = rec[i].ma_x[0], tyl = rec[i].mi_n[1], tyr = rec[i].ma_x[1];
int hit = query(root, 0, i, 0, INF, 0, INF);
if (hit != 0)
{
ans[t[hit].n] = rec[i].n;
t[hit].n = 0;
adjust(hit);
}
}
}
void output_ans()
{
for (int i = 1; i <= m; i++)
printf("%d\n", ans[i]);
}
int main()
{
//freopen("F:\\rush.txt", "r", stdin);
input_data();
get_ans();
output_ans();
return 0;
}