BZOJ2648: SJY擺棋子 && 暴力kdtree學習筆記

題目

BZOJ2648: SJY擺棋子

題解

其實就是kdt學習筆記。。
把平面上n個點用一種奇怪的方式,弄成一棵樹結構,然後保證插入,詢問某個點的最近點,的複雜度都不會太高(起碼得比暴力優),於是kdt誕生了,其實kdt比這個強大多了,可以查找d維的k近點。
方式是什麼?我突然想起一道題叫做,ZJOI2016旅行者。記得橫豎的分治方法嗎。(沒見過這道題的請忽略)。
爲了保證樹不會太高,最好是log級別的,這裏劃分樹結構的方式是這樣的,比如先從x維開始劃,找到這堆點的x的中位數,然後根據這個劃成兩堆,而中間那個作爲中位數的點,就成爲樹這個位置的節點了,並且需要保存它控制的這一堆的矩形邊界。然後換成y維繼續做剩下的兩堆,做完再換成x維balabala。看起來很有理有據,,事實上也的確是這樣要不怎麼會被髮明出來。

下面說一說如何找一個點對應的最近點。首先從根開始,先用這個節點對應的點更新一下ans,然後分別計算一下詢問的點到左兒子和右兒子所控制矩形的距離(在矩形內部則爲0,不存在則爲INF),這是個下界,如果預估出來的值小於ans那麼進入這個兒子遞歸詢問,並且優先詢問預估值比較小的兒子(這一步可以更新ans,有點像A*搜索,可以剪枝)。不難發現不剪枝是O(n) 的(雖然剪了據說也能被卡,詳見這道題的discuss),剪了之後據說是隨機數據期望O(logn) 的,能被卡到O(n) (不過能不能被卡到O(n) )我也不太清楚,,因爲貌似沒有解釋kdt複雜度的文章,如果哪位找到了請一定告訴我謝謝qwq。

如何插入呢,據說是暴力插然後用替罪羊重構的思想,因爲yy了一下感覺暴力插可以被卡到O(n) ,wkl大爺說可以rotate。。(orz),其實這道題暴力插可以過。就判斷一下插入點和當前節點某一維大小關係,就是二叉搜索樹的插入。

,這題一點都不給O(nlogn2) 的cdq+線段樹活路。

代碼

//QWsin
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int INF=1<<29;

const int maxn=500000+10;

inline int read()
{
    int ret=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0';
    return ret;
}

int n,m;

struct Node{
    int M[2],m[2],d[2];
    Node *ch[2];
    Node(){M[0]=M[1]=d[0]=d[1]=0;m[0]=m[1]=INF;ch[0]=ch[1]=NULL;}
    inline void modify(int dd[2]){M[0]=m[0]=dd[0];M[1]=m[1]=dd[1];d[0]=dd[0];d[1]=dd[1];}
    inline void up(Node *c){
        M[0]=max(M[0],c->M[0]);M[1]=max(M[1],c->M[1]);  
        m[0]=min(m[0],c->m[0]);m[1]=min(m[1],c->m[1]);
    }
}*root;

int xy[maxn][2],id[maxn];//xy[i][0]=x xy[i][1]=y

int D;
inline int cmp(const int &a,const int &b){
    return (xy[a][D] < xy[b][D])||(xy[a][D]==xy[b][D]&&xy[a][D^1] < xy[b][D^1]);
}

void build(Node* &p,int l,int r,int t)
{
    int mid=(l+r)>>1;D=t;
    nth_element(id+l,id+mid,id+r+1,cmp);
    p=new Node();p->modify(xy[id[mid]]);
    if(l!=mid) build(p->ch[0],l,mid-1,t^1),p->up(p->ch[0]);
    if(r!=mid) build(p->ch[1],mid+1,r,t^1),p->up(p->ch[1]);
}

int ite[2];
void insert()
{
    Node *cur=root,*tmp=new Node();tmp->modify(ite);
    for(int D=0;;D^=1)
    {
        cur->up(tmp);
        int t=ite[D] > cur->d[D];
        if(cur->ch[t] == NULL) {cur->ch[t]=tmp;return ;}
        else cur=cur->ch[t];
    }
}

inline int dis(int d[2],Node *p)
{
    int ret=0;
    ret+=(d[0] < p->m[0]) ? p->m[0]-d[0] : 0;
    ret+=(d[0] > p->M[0]) ? d[0]-p->M[0] : 0;
    ret+=(d[1] < p->m[1]) ? p->m[1]-d[1] : 0;
    ret+=(d[1] > p->M[1]) ? d[1]-p->M[1] : 0;
    return ret;
}

int ans;
void query(Node* p)
{
    if(!p) return ;
    ans=min(ans,abs(p->d[0] - ite[0]) + abs(p->d[1] - ite[1]));
    int t0=p->ch[0]?dis(ite,p->ch[0]):INF;
    int t1=p->ch[1]?dis(ite,p->ch[1]):INF;
    if(t0 < t1){
        if(t0 < ans) query(p->ch[0]);
        if(t1 < ans) query(p->ch[1]);
    }
    else{
        if(t1 < ans) query(p->ch[1]);
        if(t0 < ans) query(p->ch[0]);   
    }
}

inline void init_data()
{
    cin>>n>>m;
    for(int i=1;i<=n;++i) xy[i][0]=read(),xy[i][1]=read(),id[i]=i ;
    build(root,1,n,0);
}

inline void solve()
{
    int op;
    while(m--)
    {
        op=read();ite[0]=read();ite[1]=read();
        if(op==1) insert();
        else {
            ans=INF;query(root);
            printf("%d\n",ans);
        }
    }
}

int main()
{
    init_data();
    solve();
    return 0;
}
發佈了167 篇原創文章 · 獲贊 76 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章