[BZOJ2648]SJY摆棋子/[BZOJ2716][Violet 3]天使玩偶:K-D Tree

分析:

本来写的是一开始把所有结点都预先建好,但是T了。
orz了一发题解后发现需要像替罪羊树一样将不平衡的子树重构。
但是不应该是重构深度最小的不平衡的节点为根的子树吗?

代码:

预先开点(TLE)(洛谷AC):

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
inline LL read(){
    LL x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f; 
}
const int MAXN=300005;
int n,m,qst[MAXN][3],tot;
int root,D;
int qx,qy,ans=1e9;
struct KD_Tree{
    int ch[2],fa;
    int x,y;
    int id;
    bool exi;
    int xx[2],yy[2];
    friend bool operator < (KD_Tree x,KD_Tree y){
        if(D==0) return x.x==y.x?x.y<y.y:x.x<y.x;
        else return x.y==y.y?x.x<y.x:x.y<y.y;
    }
}a[MAXN<<1];
#define mid ((l+r)>>1)
#define lc a[x].ch[0]
#define rc a[x].ch[1]
inline void pushup(int x){
    if(x&&a[x].exi){
        a[x].xx[0]=min(a[x].xx[0],a[x].x);
        a[x].xx[1]=max(a[x].xx[1],a[x].x);
        a[x].yy[0]=min(a[x].yy[0],a[x].y);
        a[x].yy[1]=max(a[x].yy[1],a[x].y);
    }
    if(lc){
        a[x].xx[0]=min(a[x].xx[0],a[lc].xx[0]);
        a[x].xx[1]=max(a[x].xx[1],a[lc].xx[1]);
        a[x].yy[0]=min(a[x].yy[0],a[lc].yy[0]);
        a[x].yy[1]=max(a[x].yy[1],a[lc].yy[1]);
    }
    if(rc){
        a[x].xx[0]=min(a[x].xx[0],a[rc].xx[0]);
        a[x].xx[1]=max(a[x].xx[1],a[rc].xx[1]);
        a[x].yy[0]=min(a[x].yy[0],a[rc].yy[0]);
        a[x].yy[1]=max(a[x].yy[1],a[rc].yy[1]);
    }
}
int buildKD(int l,int r,int d){
    if(l>r) return 0;
    D=d;nth_element(a+l,a+mid,a+r+1);
    a[mid].ch[0]=buildKD(l,mid-1,d^1);
    a[mid].ch[1]=buildKD(mid+1,r,d^1);
    if(a[mid].ch[0]) a[a[mid].ch[0]].fa=mid;
    if(a[mid].ch[1]) a[a[mid].ch[1]].fa=mid;
    pushup(mid);
    return mid;
}
inline void Insert(int x){
    a[x].exi=1;int y=x;pushup(y);
    while(a[y].fa) pushup(y=a[y].fa);
}
inline int Evaluate(int x){
    if(!x||a[x].xx[0]>a[x].xx[1]) return 1e9;
    return (abs(qx-a[x].xx[0])+abs(qx-a[x].xx[1])-(a[x].xx[1]-a[x].xx[0]))/2+
        (abs(qy-a[x].yy[0])+abs(qy-a[x].yy[1])-(a[x].yy[1]-a[x].yy[0]))/2;
}
void query(int x){
    if(!x||Evaluate(x)>=ans) return;
    if(a[x].exi) ans=min(ans,abs(qx-a[x].x)+abs(qy-a[x].y));
    if(Evaluate(lc)<Evaluate(rc)) query(lc),query(rc);
    else query(rc),query(lc);
}
#undef mid
#undef lc
#undef rc
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++){
        a[i].x=read(),a[i].y=read();
        a[i].xx[0]=a[i].xx[1]=a[i].x;
        a[i].yy[0]=a[i].yy[1]=a[i].y;
        a[i].exi=1;
    }
    tot=n;
    for(int i=1;i<=m;i++){
        qst[i][0]=read();
        qst[i][1]=read();
        qst[i][2]=read();
        if(qst[i][0]==1){
            tot++;
            a[tot].x=qst[i][1],a[tot].y=qst[i][2];
            a[tot].xx[0]=a[tot].yy[0]=1e9;
            a[tot].xx[1]=a[tot].yy[1]=-1;
            a[tot].exi=0;a[tot].id=i;
        }
    }
    root=buildKD(1,tot,0);
    for(int i=1;i<=tot;i++) qst[a[i].id][1]=i;
    for(int i=1;i<=m;i++){
        if(qst[i][0]==1) Insert(qst[i][1]);
        else{
            qx=qst[i][1],qy=qst[i][2];
            ans=1e9;query(root);
            printf("%d\n",ans);
        }
    }
    return 0;
}

带重构(洛谷题解):

#include<bits/stdc++.h>
using namespace std;
int read() {
    int q=0,w=1;char ch=' ';
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
    return q*w;
}
#define alph (0.75)
const int N=1000005;
struct point{int x[2];}p[N];
struct node{int mi[2],mx[2],ls,rs,sz;point tp;}tr[N];
int n,m,rt,cur,top,WD,ans,rub[N];
int operator < (point a,point b) {return a.x[WD]<b.x[WD];}
int newnode() {//建立新节点
    if(top) return rub[top--];
    else return ++cur;
}
void up(int k) {
    int l=tr[k].ls,r=tr[k].rs;
    for(int i=0;i<=1;++i) {
        tr[k].mi[i]=tr[k].mx[i]=tr[k].tp.x[i];
        if(l) tr[k].mi[i]=min(tr[k].mi[i],tr[l].mi[i]),tr[k].mx[i]=max(tr[k].mx[i],tr[l].mx[i]);
        if(r) tr[k].mi[i]=min(tr[k].mi[i],tr[r].mi[i]),tr[k].mx[i]=max(tr[k].mx[i],tr[r].mx[i]);
    }
    tr[k].sz=tr[l].sz+tr[r].sz+1;
}
int build(int l,int r,int wd) {
    if(l>r) return 0;
    int k=newnode(),mid=(l+r)>>1;
    WD=wd,nth_element(p+l,p+mid,p+r+1),tr[k].tp=p[mid];
    //nth_element:使得序列某一位置x(此处是p+mid)是该序列的第x大数
    tr[k].ls=build(l,mid-1,wd^1),tr[k].rs=build(mid+1,r,wd^1);
    up(k);return k;
}
void pia(int k,int num) {//拍扁
    if(tr[k].ls) pia(tr[k].ls,num);
    p[num+tr[tr[k].ls].sz+1]=tr[k].tp,rub[++top]=k;
    if(tr[k].rs) pia(tr[k].rs,num+tr[tr[k].ls].sz+1);
}
void check(int &k,int wd) {//检查子树是否不平衡
    if(alph*tr[k].sz<tr[tr[k].ls].sz||alph*tr[k].sz<tr[tr[k].rs].sz)
        pia(k,0),k=build(1,tr[k].sz,wd);
}
void ins(point tmp,int &k,int wd) {//插入
    if(!k) {k=newnode(),tr[k].tp=tmp,tr[k].ls=tr[k].rs=0,up(k);return;}
    if(tr[k].tp.x[wd]<tmp.x[wd]) ins(tmp,tr[k].rs,wd^1);
    else ins(tmp,tr[k].ls,wd^1);
    up(k),check(k,wd);//记得在check之前要先pushup
}
int getdis(point tmp,int k) {//获得当前点到矩形的曼哈顿距离
    int re=0;
    for(int i=0;i<=1;++i)
        re+=max(0,tmp.x[i]-tr[k].mx[i])+max(0,tr[k].mi[i]-tmp.x[i]);
    return re;
}
int dist(point a,point b) {return abs(a.x[0]-b.x[0])+abs(a.x[1]-b.x[1]);}
void query(point tmp,int k) {//查询
    ans=min(ans,dist(tmp,tr[k].tp));
    int dl=INT_MAX,dr=INT_MAX;
    if(tr[k].ls) dl=getdis(tmp,tr[k].ls);
    if(tr[k].rs) dr=getdis(tmp,tr[k].rs);
    if(dl<dr) {
        if(dl<ans) query(tmp,tr[k].ls);
        if(dr<ans) query(tmp,tr[k].rs);
    }
    else {
        if(dr<ans) query(tmp,tr[k].rs);
        if(dl<ans) query(tmp,tr[k].ls);
    }
}
int main() {
    int bj;
    n=read(),m=read();
    for(int i=1;i<=n;++i) p[i].x[0]=read(),p[i].x[1]=read();
    rt=build(1,n,0);
    while(m--) {
        point tmp;
        bj=read(),tmp.x[0]=read(),tmp.x[1]=read();
        if(bj==1) ins(tmp,rt,0);
        else ans=INT_MAX,query(tmp,rt),printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ErkkiErkko/p/9615582.html