K-D Tree:高维空间,我就是唯一!

首先是普通平衡树(替罪羊树):

#include<bits/stdc++.h>
using namespace std;

const int INF=0x7fffffff;
const int N=2e6+10;

struct ScapegoatTree
{
    int l,r,val;
    int sz,sd;//树中总点数,树中不包含已被删除的点的点数,用于判断是否重构
    int cnt;//该数据出现次数,为0表示已删除
    int size;//平衡树中存在的数据个数(即每个点记cnt次)
}tr[N];

double alpha=0.75;
int root,tot,tzy[N];
int New(int val)
{
    tr[++tot]={0,0,val,1,1,1,1};
    return tot;
}
void pushup(int p)
{
    tr[p].sz=tr[tr[p].l].sz+tr[tr[p].r].sz+1;
    tr[p].sd=tr[tr[p].l].sd+tr[tr[p].r].sd+(tr[p].cnt!=0);
    tr[p].size=tr[tr[p].l].size+tr[tr[p].r].size+tr[p].cnt;
}
int build(int l,int r)//将tzy数组中l~r的节点重新建成一棵树
{
    if(l>r)return 0;
    int mid=l+r>>1;
    tr[tzy[mid]].l=build(l,mid-1);
    tr[tzy[mid]].r=build(mid+1,r);
    pushup(tzy[mid]);
    return tzy[mid];
}
void dfs(int p,int &num)//将以p为根的子树的节点取出
{
    if(!p)return;
    dfs(tr[p].l,num);
    if(tr[p].cnt)tzy[++num]=p;
    dfs(tr[p].r,num);
}
void rebuild(int &p)
{
    if(!tr[p].cnt)return;//当前节点不存在,无法重建
    if(max(tr[tr[p].l].sz,tr[tr[p].r].sz)<=alpha*tr[p].sz)return;//不平衡程度在可接受范围内
    if(tr[p].sd<=tr[p].sz*alpha)return;//不存在的点的数量在可接受范围内
    int num=0;dfs(p,num);
    p=build(1,num);
}
void insert(int &p,int val)
{
    if(!p){p=New(val);return;}
    if(tr[p].val==val)++tr[p].cnt;
    else if(tr[p].val>val)insert(tr[p].l,val);
    else insert(tr[p].r,val);
    pushup(p);rebuild(p);
}
void erase(int &p,int val)//惰性删除
{
    if(!p)return;
    if(tr[p].val==val&&tr[p].cnt)--tr[p].cnt;
    else if(tr[p].val>val)erase(tr[p].l,val);
    else erase(tr[p].r,val);
    pushup(p);rebuild(p);
}

//以上操作很重要······

int get_rank(int p,int val)
{
    if(!p)return 1;
    if(tr[p].val==val)return tr[tr[p].l].size+1;
    if(tr[p].val>val)return get_rank(tr[p].l,val);
    return tr[tr[p].l].size+tr[p].cnt+get_rank(tr[p].r,val);
}
int get_val(int p,int rank)//不存在返回int最大值
{
    if(!p)return INF;
    if(tr[tr[p].l].size>=rank)return get_val(tr[p].l,rank);
    if(tr[tr[p].l].size+tr[p].cnt>=rank)return tr[p].val;
    return get_val(tr[p].r,rank-tr[tr[p].l].size-tr[p].cnt);
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0);

    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;++i)cin>>tzy[i];
    sort(tzy+1,tzy+n+1);
    int num=0;
    for(int i=1;i<=n;++i)
    {
        if(num>0&&tzy[i]==tr[tzy[num]].val)
            ++tr[tzy[num]].cnt;
        else tzy[++num]=New(tzy[i]);
    }
    root=build(1,num);//对初始值手动建树

    int last=0,ans=0;
    while(m--)
    {
        int op,x;
        cin>>op>>x;
        x^=last;
        if(op==1)insert(root,x);
        else if(op==2)erase(root,x);
        else if(op==3)last=get_rank(root,x);
        else if(op==4)last=get_val(root,x);
        else if(op==5)last=get_val(root,get_rank(root,x)-1);
        else last=get_val(root,get_rank(root,x+1));
        if(op>=3)ans^=last;
    }
    cout<<ans<<'\n';

    return 0;
}

现在,我们一起来感受一下K-D Tree的魅力吧!!!
简单题

#pragma GCC optimize(3,"Ofast","inline")

#include<bits/stdc++.h>
using namespace std;

namespace _KDTree{

const int N=5e5+10;
const int K=3;//树中为K-1维空间的点,如果维度增加,修改K即可
const double alpha=0.725;
int root,tot,kd;
int st[N],top;

struct Point{int num[K],val;};
struct Node
{
    int l,r,d;//d表示子树以维度d进行划分
    int size,sum;//sum记录子树点上的权值和
    int minn[K],maxn[K];//记录子树中点的坐标在每个维度上的最小值和最大值
    Point p;
}tr[N];

#define ls tr[now].l
#define rs tr[now].r

void pushup(int now)//更新size,sum,minn,maxn
{
    for(int i=1;i<K;++i)
    {
        tr[now].minn[i]=tr[now].maxn[i]=tr[now].p.num[i];
        if(ls){
            tr[now].minn[i]=min(tr[now].minn[i],tr[ls].minn[i]);
            tr[now].maxn[i]=max(tr[now].maxn[i],tr[ls].maxn[i]);
        }
        if(rs){
            tr[now].minn[i]=min(tr[now].minn[i],tr[rs].minn[i]);
            tr[now].maxn[i]=max(tr[now].maxn[i],tr[rs].maxn[i]);
        }
    }
    tr[now].sum=tr[ls].sum+tr[rs].sum+tr[now].p.val;
	tr[now].size=tr[ls].size+tr[rs].size+1;
}

bool cmp(int a,int b)//辅助build
{
    return tr[a].p.num[kd]<tr[b].p.num[kd];
}
int build(int l,int r)//建树
{
    if(l>r)return 0;
    int mid=l+r>>1;
    double Max=-1;
    for(int i=1;i<K;++i)
    {
        double avg=0;//平均数
        for(int j=l;j<=r;++j)avg+=tr[st[j]].p.num[i];
        avg/=(r-l+1);
        double var=0;//求当前维度上的方差
        for(int j=l;j<=r;++j)var+=(tr[st[j]].p.num[i]-avg)*(tr[st[j]].p.num[i]-avg);
        if(var>Max)
        {
            Max=var;
            kd=i;
        }
    }

    nth_element(st+l,st+mid,st+r+1,cmp);
    tr[st[mid]].d=kd;
    tr[st[mid]].l=build(l,mid-1);
    tr[st[mid]].r=build(mid+1,r);
    pushup(st[mid]);
    return st[mid];
}
void dfs(int now)//收集子树节点编号
{
    if(!now)return;
    dfs(ls);
    st[++top]=now;
    dfs(rs);
}
void rebuild(int &now)//重构
{
    if(max(tr[ls].size,tr[rs].size)<alpha*tr[now].size)return;
    top=0;dfs(now);
    now=build(1,top);
}
//以上为核心操作,是保证时间复杂度的关键······

void insert(int &now,Point p)
{
    if(!now)
    {
        now=++tot;
        tr[now].p=p;
        tr[now].d=(unsigned)p.val%(K-1)+1;//随机一个划分维度
        pushup(now);
        return;
    }
    int d=tr[now].d;
    if(tr[now].p.num[d]>=p.num[d])insert(ls,p);
    else insert(rs,p);
    pushup(now);rebuild(now);
}

//给出查询矩形的左下角和右上角
bool inside(int x1,int y1,int x2,int y2,int a,int b,int c,int d){
    return x1<=a&&y1<=b&&x2>=c&&y2>=d;//子树矩形被查询矩形完全包含
}
bool outside(int x1,int y1,int x2,int y2,int a,int b,int c,int d){
    return x1>c||x2<a||y1>d||y2<b;//子树矩形与查询矩形无交集
}
int query(int now,int x1,int y1,int x2,int y2)
{
    if(outside(x1,y1,x2,y2,tr[now].minn[1],tr[now].minn[2],tr[now].maxn[1],tr[now].maxn[2]))return 0;
    if(inside(x1,y1,x2,y2,tr[now].minn[1],tr[now].minn[2],tr[now].maxn[1],tr[now].maxn[2]))return tr[now].sum;
    int ans=0;
    if(inside(x1,y1,x2,y2,tr[now].p.num[1],tr[now].p.num[2],tr[now].p.num[1],tr[now].p.num[2]))ans+=tr[now].p.val;
    ans+=query(ls,x1,y1,x2,y2)+query(rs,x1,y1,x2,y2);
    return ans;
}

#undef ls
#undef rs
}using namespace _KDTree;

int main()
{
    ios::sync_with_stdio(0),cin.tie(0);

    int n;cin>>n;//一句废话,没啥用
    
    Point p;
    int x1,y1,x2,y2;
    int op,lastans=0;
    while(cin>>op&&op!=3)
    {
        if(op==1)
        {
            cin>>p.num[1]>>p.num[2]>>p.val;
            p.num[1]^=lastans;
            p.num[2]^=lastans;
            p.val^=lastans;
            insert(root,p);
        }
        else if(op==2)
        {
            cin>>x1>>y1>>x2>>y2;
            x1^=lastans;
            y1^=lastans;
            x2^=lastans;
            y2^=lastans;
            cout<<(lastans=query(root,x1,y1,x2,y2))<<'\n';
        }
    }

    return 0;
}

K-D Tree本质上是一种暴力的数据结构,但可以通过剪枝或者启发式搜索等优化技巧来降低期望复杂度,虽然很多时候最坏情况下的时间复杂度还是可以让你T飞
k远点对

#pragma GCC optimize(3,"Ofast","inline")

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

priority_queue<ll,vector<ll>,greater<ll>>q;

namespace _KDTree{//本题没有插入和删除操作,无需重构。

const int N=5e5+10;
const int K=3;//树中为K-1维空间的点,如果维度增加,修改K即可
int root,tot,kd;
int st[N],top;

struct Point{int num[K];}p[N];//这次不将坐标点直接放在树上的点中,不然坐标点还要再存一次
struct Node
{
    int l,r;
    int minn[K],maxn[K];//记录子树中点的坐标在每个维度上的最小值和最大值
    int id;//记录坐标点的编号
}tr[N];

#define ls tr[now].l
#define rs tr[now].r

void pushup(int now)//更新minn,maxn
{
    for(int i=1;i<K;++i)
    {
        tr[now].minn[i]=tr[now].maxn[i]=p[tr[now].id].num[i];
        if(ls){
            tr[now].minn[i]=min(tr[now].minn[i],tr[ls].minn[i]);
            tr[now].maxn[i]=max(tr[now].maxn[i],tr[ls].maxn[i]);
        }
        if(rs){
            tr[now].minn[i]=min(tr[now].minn[i],tr[rs].minn[i]);
            tr[now].maxn[i]=max(tr[now].maxn[i],tr[rs].maxn[i]);
        }
    }
}

bool cmp(int a,int b)//辅助build
{
    return p[tr[a].id].num[kd]<p[tr[b].id].num[kd];
}
int build(int l,int r)//建树
{
    if(l>r)return 0;
    int mid=l+r>>1;
    double Max=-1;
    for(int i=1;i<K;++i)
    {
        double avg=0;//平均数
        for(int j=l;j<=r;++j)avg+=p[tr[st[j]].id].num[i];
        avg/=(r-l+1);
        double var=0;//求当前维度上的方差
        for(int j=l;j<=r;++j)var+=(p[tr[st[j]].id].num[i]-avg)*(p[tr[st[j]].id].num[i]-avg);
        if(var>Max)
        {
            Max=var;
            kd=i;
        }
    }

    nth_element(st+l,st+mid,st+r+1,cmp);
    tr[st[mid]].l=build(l,mid-1);
    tr[st[mid]].r=build(mid+1,r);
    pushup(st[mid]);
    return st[mid];
}

ll sqr(ll x){return x*x;}
ll dist(int now,int x)//计算点与子树矩形的最远距离
{
    return max(sqr(p[x].num[1]-tr[now].minn[1]),sqr(p[x].num[1]-tr[now].maxn[1]))+
           max(sqr(p[x].num[2]-tr[now].minn[2]),sqr(p[x].num[2]-tr[now].maxn[2]));
}
void query(int now,int x)
{
    if(!now)return;
    ll t=sqr(p[tr[now].id].num[1]-p[x].num[1])+sqr(p[tr[now].id].num[2]-p[x].num[2]);
    if(t>q.top())q.pop(),q.push(t);
    ll disl=dist(ls,x),disr=dist(rs,x);
    if(disl>q.top()&&disr>q.top())//启发式搜索,优先搜索距离值更大的子树,这样另一棵子树就可能不会再搜索
    {
        if(disl>disr)
        {
            query(ls,x);
            if(disr>q.top())query(rs,x);
        }
        else
        {
            query(rs,x);
            if(disl>q.top())query(ls,x);
        }
    }
    else
    {
        if(disl>q.top())query(ls,x);
        if(disr>q.top())query(rs,x);
    } 
}

#undef ls
#undef rs
}using namespace _KDTree;

int main()
{
    ios::sync_with_stdio(0),cin.tie(0);

    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;++i)cin>>p[i].num[1]>>p[i].num[2];
    for(int i=1;i<=n;++i)
    {
        ++tot;
        tr[tot].id=i;
        st[++top]=tot;
    }
    k*=2;
    root=build(1,top);
    for(int i=1;i<=k;++i)q.push(0);
    for(int i=1;i<=n;++i)query(root,i);
    cout<<q.top()<<'\n';
    return 0;
}

完结撒花!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值