• 【BZOJ2648】SJY摆棋子(KDTree)


    点此看题面

    大致题意: 在一个二维平面上现有\(N\)个棋子,有两种操作:增加一个棋子;查询离某个坐标最近的棋子离它的曼哈顿距离。

    \(KD-Tree\)

    这是一道\(KD-Tree\)乱搞题

    如何询问

    在此题中,建树、插入、重构等过程其实与普通的\(KD-Tree\)是一样的,关键就在于如何询问

    对于当前处理到的节点,第一步当然是更新\(ans\)\(ans=min(ans,node[x].get\_dis(v))\)

    由于\(KD-Tree\)的性质,所以当前节点的两棵子树中的节点肯定位于两个互不相交的矩形中

    于是我们可以考虑求出当前询问的点到这两个矩形的距离(若在矩形内则距离为\(0\))。

    这样一来就有了第一个剪枝:只有当这个距离\(<ans\)时,我们才需要访问该子树(证明:当前询问的点到这个矩形中任意一点肯定\(\ge\)到这个矩形的距离)。

    然后,我们就可以分别求解了。

    注意,求解的顺序也是有学问的,这就是第二个剪枝:按照当前询问点到矩形的距离从小到大进行求解,这样有利于求出更小的\(ans\),从而进一步剪枝。

    这样一来,就将复杂度成功卡到了\(O(玄学)\)

    代码

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define uint unsigned int
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define abs(x) ((x)<0?-(x):(x))
    #define INF 1e9
    #define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
    #define ten(x) (((x)<<3)+((x)<<1))
    #define N 300000
    using namespace std;
    int d;
    struct point
    {
        int s[2];
        inline friend bool operator < (point x,point y) {return x.s[d]<y.s[d];}
        point(int x=0,int y=0) {s[0]=x,s[1]=y;}
    }s[N+5];
    class FIO
    {
        private:
            #define Fsize 100000
            #define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
            #define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
            int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
        public:
            FIO() {FinNow=FinEnd=Fin;}
            inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
            inline void read_char(char &x) {while(isspace(x=tc()));}
            inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
            inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
            inline void write_char(char x) {pc(x);}
            inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
            inline void end() {fwrite(Fout,1,FoutSize,stdout);}
    }F;
    class Class_TwoDTree//2D-Tree
    {
        private:
            #define alpha 0.75
            #define balance(x) (alpha*node[x].Size>=1.0*max(node[node[x].Son[0]].Size,node[node[x].Son[1]].Size))//判断子树是否平衡,借鉴了替罪羊树的思想
            int rt,ans,tot,cnt,Void[(N<<1)+5];point data[(N<<1)+5];
            struct Tree
            {
                int MaxX,MinX,MaxY,MinY,Size,Son[2];
                point p;
                Tree(int x=0,int y=0):p(x,y),MaxX(x),MinX(x),MaxY(y),MinY(y){Son[0]=Son[1]=Size=0;}
                inline int get_dis(point w) {return abs(p.s[0]-w.s[0])+abs(p.s[1]-w.s[1]);}//求出某个点到当前点的距离
                inline int calc(point w) {return max(MinX-w.s[0],0)+max(w.s[0]-MaxX,0)+max(MinY-w.s[1],0)+max(w.s[1]-MaxY,0);}//求出一个点到该子树构成矩形的距离
            }node[(N<<1)+5];
            inline void PushUp(int x)//上传信息
            {
            	node[x].Size=node[node[x].Son[0]].Size+node[node[x].Son[1]].Size+1,
                node[x].MaxX=max(node[x].p.s[0],max(node[node[x].Son[0]].MaxX,node[node[x].Son[1]].MaxX)),node[x].MinX=min(node[x].p.s[0],min(node[node[x].Son[0]].MinX,node[node[x].Son[1]].MinX)),
                node[x].MaxY=max(node[x].p.s[1],max(node[node[x].Son[0]].MaxY,node[node[x].Son[1]].MaxY)),node[x].MinY=min(node[x].p.s[1],min(node[node[x].Son[0]].MinY,node[node[x].Son[1]].MinY));
            }
            inline int Build(int l,int r,int op)//建树
            {
                register int x=Void[tot--],mid=l+r>>1;
                d=op,nth_element(data+l+1,data+mid+1,data+r+1),node[x]=Tree(data[mid].s[0],data[mid].s[1]),
                l<mid&&(node[x].Son[0]=Build(l,mid-1,op^1)),r>mid&&(node[x].Son[1]=Build(mid+1,r,op^1));
                return (void)(PushUp(x)),x;
            }
            inline void Traversal(int x) {if(x) Traversal(node[x].Son[0]),data[++cnt]=node[Void[++tot]=x].p,Traversal(node[x].Son[1]);}//中序遍历
            inline void ReBuild(int &x,int op) {cnt=0,Traversal(x),x=Build(1,cnt,op);}//重构
            inline void ins(int &x,point v,int op)//插入元素
            {
                if(!x) return (void)(node[x=Void[tot--]]=Tree(v.s[0],v.s[1]),node[x].Size=1);
                v.s[op]<node[x].p.s[op]?ins(node[x].Son[0],v,op^1):ins(node[x].Son[1],v,op^1);
            	PushUp(x);
            }
            inline void check(int &x,point v,int op)//判断是否平衡
            {
                if(!x) return;if(!balance(x)) return ReBuild(x,op);
                v.s[op]<node[x].p.s[op]?check(node[x].Son[0],v,op^1):check(node[x].Son[1],v,op^1);
            }
            inline void qry(int x,point v)//询问
            {
                register int res=node[x].get_dis(v),dis1=node[x].Son[0]?node[node[x].Son[0]].calc(v):INF,dis2=node[x].Son[1]?node[node[x].Son[1]].calc(v):INF;//res求出当前点的距离,dis1和dis2分别求出当前询问点到两个子树构成矩形的距离
                ans=min(ans,res);//更新ans
                if(dis1<dis2) dis1<ans&&(qry(node[x].Son[0],v),0),dis2<ans&&(qry(node[x].Son[1],v),0);//注意剪枝
                else dis2<ans&&(qry(node[x].Son[1],v),0),dis1<ans&&(qry(node[x].Son[0],v),0);
            }
        public:
            Class_TwoDTree() {node[0].MaxX=node[0].MaxY=-INF,node[0].MinX=node[0].MinY=INF;for(register int i=N<<1;i;--i) Void[++tot]=i;}//初始化
            inline void Init(int n,point *num) {for(register int i=1;i<=n;++i) data[i]=num[i];rt=Build(1,n,0);}//建树
            inline void Insert(point v) {ins(rt,v,0),check(rt,v,0);}//插入元素
            inline int Query(point v) {return (void)(ans=INF,qry(rt,v)),ans;}//询问
    }TwoDTree;
    int main()
    {
        register int i,n,Q,op,x,y;
        for(F.read(n),F.read(Q),i=1;i<=n;++i) F.read(x),F.read(y),s[i]=point(x,y);
        for(TwoDTree.Init(n,s);Q;--Q)
        {
        	static int v=0;
        	F.read(op),F.read(x),F.read(y);
        	if(op^1) F.write(TwoDTree.Query(point(x,y))),F.write_char('\n'),++v;
        	else TwoDTree.Insert(point(x,y));
        }
        return F.end(),0;
    }
    
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    密码-散乱的密文
    设置nginx服务器
    Postman设置authorization
    mongodb 学习笔记 1
    一道面试题,观察者模式
    laravel-admin form组件
    laravel-admin 管理平台获取当前登陆用户信息
    Laravel-admin安装富文本编辑器 WangEditor 上传图片到服务器,而不是按BASE64保存
    Laravel报错Whoops, looks like something went wrong 解决办法
    菜鸟用composer 安装项目依赖 vendor:当拿到一个Laravel项目时怎么配置本地环境
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2648.html
Copyright © 2020-2023  润新知