题解:P2093 [国家集训队] JZPFAR
今天,我,学会了,K-D Tree。
简单讲一下这个数据结构的流程:我们把平面上的点按照某个维度上过某个点的一条分割线切开,以分割线穿过的这个点为根,递归处理左右两个子树。这样静态建出的树高度是 \(O(\log n)\) 当然是理想的,但是插入的时候会影响平衡性。所以可以用一些替罪羊的技巧,每插入若干个点重构一次,即可保证复杂度正确。
这个东西能做什么?可以在 \(O(n ^ \frac{1}{2})\) 的时间内做平面上的点对查询。
然后你就会发现这个题目就是 KDT 的模板。具体地,我们要在一个点中维护这个点所覆盖的矩形中最靠上、下、左、右的点。然后由于我们要求前 \(k\) 大,\(k\) 又非常小,我们可以用一个大根堆来存前 \(k\) 大的点,其中堆顶是第 \(k\) 大。我们从根节点开始查询,先看看当前节点的这个点是不是当前的前 \(k\) 大,如果是,就把它插入到堆里面。接下来,我们可以求出左、右子树中的点到目标点的最大距离。如果左或右子树有可能成为前 \(k\) 大,就递归查询即可。
注意这里的最大距离是用我们之前维护的横、纵坐标的最大和最小值算出来的,因此会 \(\ge\) 实际上的最大距离,可以认为是被加入堆中的一个必要条件。
那么这道题就做完了,复杂度 \(O(n \sqrt n \log k)\)。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define N 100006
using namespace std;
int n,q,rt;
typedef pair<int,int> pii;
const double REVAL=0.75;
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(int &x,int y){x=x<y?y:x;}
int cmpx(int i,int j);
int cmpy(int i,int j);
struct KDT {
int g[N],tot;
struct Node {
int val,sum,sz,ls,rs;
int l,r,u,d,x,y,dir;
Node(){val=sum=sz=ls=rs=l=r=u=d=x=y=dir=0;}
}tree[N];
priority_queue<pii,vector<pii>,greater<pii> > q;
void push_up(int p)
{
int ls=tree[p].ls,rs=tree[p].rs;
tree[p].sum=tree[ls].sum+tree[rs].sum+tree[p].val;
tree[p].sz=tree[ls].sz+tree[rs].sz+1;
tree[p].l=tree[p].r=tree[p].x,tree[p].u=tree[p].d=tree[p].y;
if(ls)
{
chkmin(tree[p].l,tree[ls].l),chkmax(tree[p].r,tree[ls].r);
chkmin(tree[p].d,tree[ls].d),chkmax(tree[p].u,tree[ls].u);
} if(rs) {
chkmin(tree[p].l,tree[rs].l),chkmax(tree[p].r,tree[rs].r);
chkmin(tree[p].d,tree[rs].d),chkmax(tree[p].u,tree[rs].u);
}
}
int build(int l,int r)
{
if(l>r)return 0;
int mid=l+r>>1;
double av1=0,av2=0,s1=0,s2=0;
for(int i=l;i<=r;i++)av1+=tree[g[i]].x,av2+=tree[g[i]].y;
av1/=r-l+1,av2/=r-l+1;
for(int i=l;i<=r;i++)
s1+=(tree[g[i]].x-av1)*(tree[g[i]].x-av1);
for(int i=l;i<=r;i++)
s2+=(tree[g[i]].y-av2)*(tree[g[i]].y-av2);
if(s1>s2)nth_element(g+l,g+mid,g+r+1,cmpx),tree[g[mid]].dir=1;
else nth_element(g+l,g+mid,g+r+1,cmpy),tree[g[mid]].dir=2;
tree[g[mid]].ls=build(l,mid-1);
tree[g[mid]].rs=build(mid+1,r);
return push_up(g[mid]),g[mid];
}
void getord(int p)
{
if(!p)return;
getord(tree[p].ls),g[++tot]=p,getord(tree[p].rs);
}
void rebuild(int &p){tot=0,getord(p),p=build(1,tot);}
void insert(int &p,int v)
{
if(!p)return p=v,push_up(p),(void)0;
if(tree[p].dir==1)
{
if(tree[v].x<=tree[p].x)insert(tree[p].ls,v);
else insert(tree[p].rs,v);
} else {
if(tree[v].y<=tree[p].y)insert(tree[p].ls,v);
else insert(tree[p].rs,v);
}
push_up(p);
int ls=tree[p].ls,rs=tree[p].rs;
if(tree[p].sz*REVAL<1.0*max(tree[ls].sz,tree[rs].sz))rebuild(p);
}
int get_dis(int x1,int y1,int x2,int y2)
{
int ret=(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
return ret;
}
int get_dis(int p,int x,int y)
{
int dx=max(abs(x-tree[p].l),abs(x-tree[p].r));
int dy=max(abs(y-tree[p].u),abs(y-tree[p].d));
return dx*dx+dy*dy;
}
void query(int p,int x,int y)
{
if(!p)return;
pii cur=make_pair(get_dis(x,y,tree[p].x,tree[p].y),-p);
if(cur>q.top())q.pop(),q.push(cur);
int lmax=-1e15,rmax=-1e15,ls=tree[p].ls,rs=tree[p].rs;
if(ls)lmax=get_dis(ls,x,y);
if(rs)rmax=get_dis(rs,x,y);
if(lmax<rmax)swap(lmax,rmax),swap(ls,rs);
if(lmax>q.top().first)query(ls,x,y);
if(rmax>q.top().first)query(rs,x,y);
}
}T;
int cmpx(int i,int j){return T.tree[i].x<T.tree[j].x;}
int cmpy(int i,int j){return T.tree[i].y<T.tree[j].y;}
main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&T.tree[i].x,&T.tree[i].y),T.insert(rt,i);
scanf("%lld",&q);
while(q--)
{
int x,y,k;scanf("%lld%lld%lld",&x,&y,&k);
while(T.q.size())T.q.pop();
for(int i=1;i<=k;i++)T.q.push(make_pair(-1e15,1));
T.query(rt,x,y),printf("%lld\n",-T.q.top().second);
}
return 0;
}

浙公网安备 33010602011771号