kd-tree小结

来源:互联网 发布:华为荣耀4停用移动数据 编辑:程序博客网 时间:2024/04/29 02:48

来省队集训被吊打,于是无聊学了一下kd-tree,挺好玩的东西。

kd-tree是一种支持查询平面最近点(其实是k维,但这种题目比较少)的数据结构,单次理论时间复杂度O(√n),但实际是非常快的,可以说kd-tree就是一种比较优化的暴力。

kd-tree的思想是将平面进行分割,第一次按第一维分割,第二次按第二维分割,然后接着按第一维分割,直至每个叶子节点只包含一个点,类似线段树的结构。查询时,先查询到叶子节点,然后通过回溯,对于每一个节点算出,最小的可能距离,如果小于当前的ans,就向下查询。同样,kd-tree也可以建成平衡树的模式,这样的优势是可以插入节点。


模板:

bzoj1941 http://www.lydsy.com/JudgeOnline/problem.php?id=1941

#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<iostream>#include<algorithm>#define maxn 500010#define inf 1000000000using namespace std;int n,m,cur,ans,root;int x[maxn],y[maxn];struct P{int mn[2],mx[2],d[2],lch,rch;int& operator[](int x) {return d[x];}friend bool operator<(P x,P y) {return x[cur]<y[cur];}friend int dis(P x,P y) {return abs(x[0]-y[0])+abs(x[1]-y[1]);}}p[maxn];struct kdtree{P t[maxn],T;int ans;void update(int k){int l=t[k].lch,r=t[k].rch;for (int i=0;i<2;i++){t[k].mn[i]=t[k].mx[i]=t[k][i];if (l) t[k].mn[i]=min(t[k].mn[i],t[l].mn[i]);if (r) t[k].mn[i]=min(t[k].mn[i],t[r].mn[i]);if (l) t[k].mx[i]=max(t[k].mx[i],t[l].mx[i]);if (r) t[k].mx[i]=max(t[k].mx[i],t[r].mx[i]); }}int build(int l,int r,int now){cur=now;int mid=(l+r)/2;nth_element(p+l,p+mid,p+r+1);t[mid]=p[mid];for (int i=0;i<2;i++) t[mid].mx[i]=t[mid].mn[i]=t[mid][i];if (l<mid) t[mid].lch=build(l,mid-1,now^1);if (r>mid) t[mid].rch=build(mid+1,r,now^1);update(mid);return mid;}int getmn(P x){int ans=0;for (int i=0;i<2;i++){ans+=max(T[i]-x.mx[i],0);ans+=max(x.mn[i]-T[i],0);}return ans;}int getmx(P x){int ans=0;for (int i=0;i<2;i++) ans+=max(abs(T[i]-x.mn[i]),abs(T[i]-x.mx[i]));return ans;}void querymx(int k){ans=max(ans,dis(t[k],T));int l=t[k].lch,r=t[k].rch,dl=-inf,dr=-inf;if (l) dl=getmx(t[l]);if (r) dr=getmx(t[r]);if (dl>dr){if (dl>ans) querymx(l);if (dr>ans) querymx(r);}else{if (dr>ans) querymx(r);if (dl>ans) querymx(l);}}void querymn(int k){if (dis(t[k],T)) ans=min(ans,dis(t[k],T));int l=t[k].lch,r=t[k].rch,dl=inf,dr=inf;if (l) dl=getmn(t[l]);if (r) dr=getmn(t[r]);if (dl<dr){if (dl<ans) querymn(l);if (dr<ans) querymn(r);}else{if (dr<ans) querymn(r);if (dl<ans) querymn(l);}}int query(int f,int x,int y){T[0]=x;T[1]=y;if (f==0) ans=-inf,querymx(root);else ans=inf,querymn(root);return ans;}}kdtree;int main(){scanf("%d",&n);for (int i=1;i<=n;i++){scanf("%d%d",&x[i],&y[i]);p[i][0]=x[i];p[i][1]=y[i];}root=kdtree.build(1,n,0);ans=inf;for (int i=1;i<=n;i++){int mx=kdtree.query(0,x[i],y[i]),mn=kdtree.query(1,x[i],y[i]);ans=min(ans,mx-mn);}printf("%d\n",ans);return 0;}


bzoj2716/2648 http://www.lydsy.com/JudgeOnline/problem.php?id=2716

这才是真正的kdtree模板,带上一个插入操作,类似平衡树的思想。

#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<iostream>#include<algorithm>#define maxn 1000010#define inf 1000000000 using namespace std; int n,m,root,ans,cur; int fabs(int x){    if (x>=0) return x;    else return -x;} struct P{    int d[2],mx[2],mn[2],lch,rch;    int& operator[](int x) {return d[x];}    friend bool operator<(P x,P y) {return x[cur]<y[cur];}    friend int dis(P x,P y) {return fabs(x[0]-y[0])+fabs(x[1]-y[1]);}}p[maxn]; struct kdtree{    P t[maxn],T;    int ans;    void update(int k)    {        int l=t[k].lch,r=t[k].rch;        for (int i=0;i<2;i++)        {            t[k].mn[i]=t[k].mx[i]=t[k][i];            if (l) t[k].mn[i]=min(t[k].mn[i],t[l].mn[i]);            if (r) t[k].mn[i]=min(t[k].mn[i],t[r].mn[i]);            if (l) t[k].mx[i]=max(t[k].mx[i],t[l].mx[i]);            if (r) t[k].mx[i]=max(t[k].mx[i],t[r].mx[i]);        }    }    int build(int l,int r,int now)    {        cur=now;        int mid=(l+r)/2;        nth_element(p+l,p+mid,p+r+1);        t[mid]=p[mid];        for (int i=0;i<2;i++) t[mid].mn[i]=t[mid].mx[i]=t[mid][i];        if (l<mid) t[mid].lch=build(l,mid-1,now^1);        if (r>mid) t[mid].rch=build(mid+1,r,now^1);        update(mid);        return mid;    }    int getmn(P x)    {        int tmp=0;        for (int i=0;i<2;i++)        {            tmp+=max(T[i]-x.mx[i],0);            tmp+=max(x.mn[i]-T[i],0);        }        return tmp;    }    void querymn(int k)    {        ans=min(ans,dis(t[k],T));        int l=t[k].lch,r=t[k].rch,dl=inf,dr=inf;        if (l) dl=getmn(t[l]);        if (r) dr=getmn(t[r]);        if (dl<dr)        {            if (dl<ans) querymn(l);            if (dr<ans) querymn(r);        }        else        {            if (dr<ans) querymn(r);            if (dl<ans) querymn(l);        }    }    void insert(int k,int now)    {        if (T[now]>=t[k][now])        {            if (t[k].rch) insert(t[k].rch,now^1);            else            {                t[k].rch=++n;t[n]=T;                for (int i=0;i<2;i++) t[n].mx[i]=t[n].mn[i]=t[n][i];            }        }        else        {            if (t[k].lch) insert(t[k].lch,now^1);            else            {                t[k].lch=++n;t[n]=T;                for (int i=0;i<2;i++) t[n].mx[i]=t[n].mn[i]=t[n][i];            }        }        update(k);    }    int query(int x,int y)    {        ans=inf;        T[0]=x;T[1]=y;T.lch=0;T.rch=0;        querymn(root);        return ans;    }    void insert1(int x,int y)    {        T[0]=x;T[1]=y;T.lch=0;T.rch=0;insert(root,0);    }}kdtree; int main(){    scanf("%d%d",&n,&m);    for (int i=1;i<=n;i++) scanf("%d%d",&p[i][0],&p[i][1]);    root=kdtree.build(1,n,0);    while (m--)    {        int op,x,y;        scanf("%d%d%d",&op,&x,&y);        if (op==1) kdtree.insert1(x,y);        else printf("%d\n",kdtree.query(x,y));    }    return 0;}

bzoj2850 http://www.lydsy.com/JudgeOnline/problem.php?id=2850

类似模板的算法,只不过换了一下估价函数,将曼哈顿距离换成了ax+by,维护一下子树的权值和来加速。

#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #define maxn 50010 #define inf 1000000000    using  namespace std;    int n,m,root,cur;long long a,b,c; long long ans;    struct P {     int d[2],mx[2],mn[2],lch,rch,key;     long long sum;     int& operator[](int x) {return d[x];}     friend bool operator<(P a,P b) {return a[cur]<b[cur];} }p[maxn];    int check(long long x,long long y) {     return (long long)a*x+b*y<c; }    int cal(P a) {     int tmp=0;     tmp+=check(a.mx[0],a.mx[1]);     tmp+=check(a.mx[0],a.mn[1]);     tmp+=check(a.mn[0],a.mx[1]);    tmp+=check(a.mn[0],a.mn[1]);    return tmp;}    struct kdtree {     P t[maxn];     void update(int k)     {         int l=t[k].lch,r=t[k].rch;         for (int i=0;i<2;i++)         {             t[k].mn[i]=t[k].mx[i]=t[k][i];             if (l) t[k].mn[i]=min(t[k].mn[i],t[l].mn[i]);             if (r) t[k].mn[i]=min(t[k].mn[i],t[r].mn[i]);             if (l) t[k].mx[i]=max(t[k].mx[i],t[l].mx[i]);             if (r) t[k].mx[i]=max(t[k].mx[i],t[r].mx[i]);          }         t[k].sum=(long long)t[k].key+t[l].sum+t[r].sum;    }     int build(int l,int r,int now)     {         cur=now;         int mid=(l+r)/2;         nth_element(p+l,p+mid,p+r+1);         t[mid]=p[mid];         if (l<mid) t[mid].lch=build(l,mid-1,now^1);         if (r>mid) t[mid].rch=build(mid+1,r,now^1);         update(mid);         return mid;     }     void query(int k)     {         if (check(t[k][0],t[k][1])) ans+=t[k].key;         int l=t[k].lch,r=t[k].rch,dl=0,dr=0;         if (l) dl=cal(t[l]);         if (r) dr=cal(t[r]);         if (dl==4) ans+=t[l].sum;         else if (dl) query(l);         if (dr==4) ans+=t[r].sum;         else if (dr) query(r);     } }kdtree;    int main() {     scanf("%d%d",&n,&m);     for (int i=1;i<=n;i++) scanf("%d%d%d",&p[i][0],&p[i][1],&p[i].key);     root=kdtree.build(1,n,0);     while (m--)     {         scanf("%lld%lld%lld",&a,&b,&c);         ans=0;         kdtree.query(root);         printf("%lld\n",ans);     }     return 0; } 


0 0
原创粉丝点击