bzoj 2648 SJY摆棋子

bzoj 2648 SJY摆棋子

  • 钱限题.题面可以看这里.
  • 若对每个点都算一遍距离,显然 \(T\) 爆,在此思想基础上使用 \(kd-tree\) 配合估价函数来剪枝效果较好.
  • 具体来说,对于 \(kd-tree\) 上的一个节点,我们知道它这颗子树所管辖的范围,算出我们查询的节点到这个范围内可能的最小距离(下界),若这个距离都比当前记录的答案劣或相等,再搜索这颗子树显然没有意义.
  • 这样就完成了最优性剪枝.
  • 还有一个剪枝,若左子树的距离下界小于右子树的距离下界,则应先搜索左子树,否则先搜索右子树.
  • 感性理解一下,若一个儿子的距离下界小,先搜索它,答案更有可能变得更小,如果小于或等于了另一个儿子的距离下界,就没有必要再搜另一个了
  • 对于加点的操作,就和二叉搜索树一样,从根节点往下,通过比较确定往哪边走,走到适当的位置插入即可.迭代代替递归,常数较小,但要注意走的同时用新节点更新当前节点的信息.
  • 插入过多时,由于 \(kd-tree\) 无法进行旋转操作保持平衡,树高可能会变得很大,使其退化成链,有三种方法处理.
    • 1.记录一个平衡因子 \(\alpha\) ,像替罪羊树那样,及时拍扁重构.这样写最稳定,时间上(应该是)最优的.
    • 2.不及时重构,每加入一定量的节点后对整棵树重构,这个量大概在 \(10^4\) 级别,可自行调整.这样写不太稳定,但实现非常简单,只多了几行.
    • 3.离线处理,将所有加点操作都读进来,一开始就全部建好,给每个点加个标记表示是否已经被插入(可用),真正插入时修改标记.这种写法不太推荐,每种操作都要额外考虑标记问题,比较繁琐,而且在资瓷离线的情况下,使用一些离线算法,编写难度和时间效率都远胜于 \(kd-tree\) .

\(kd-tree\) 最近点问题下大概是 \(O(玄学)\) , 处理矩形问题下最坏是 \(O(kn^{1-\frac 1 k})\) ???不会证明,有 \(dalao\) 得到了证明或证伪麻烦告知...

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pii pair<int,int>
inline int read()
{
    int x=0;
    bool pos=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar())
        if(ch=='-')
            pos=0;
    for(;isdigit(ch);ch=getchar())
        x=x*10+ch-'0';
    return pos?x:-x;
}
const int MAXN=5e5+10;
int n,m,Dimen;
int rt,K=25000;
struct node{
    int v[2];
    int mi[2],ma[2];
    int ls,rs;
    bool operator < (const node &rhs) const
        {
            return v[Dimen]<rhs.v[Dimen]; 
        }
}Tree[MAXN<<1],A[MAXN<<1];
#define root Tree[o]
#define lson Tree[root.ls]
#define rson Tree[root.rs]
#define inf 1e9
void init()
{
    for(int i=0;i<2;++i)
        {
            Tree[0].mi[i]=inf;
            Tree[0].ma[i]=-inf;
        }
}
inline void pushup(int o)
{
    for(int i=0;i<2;++i)
        {
            root.mi[i]=min(root.mi[i],min(lson.mi[i],rson.mi[i]));
            root.ma[i]=max(root.ma[i],max(lson.ma[i],rson.ma[i]));
        }
}
int BuildTree(int l,int r,int dimen)
{
    Dimen=dimen;
    int mid=(l+r)>>1;
    int o=mid;
    nth_element(A+l,A+mid,A+r+1);
    for(int i=0;i<2;++i)
        {
            root.v[i]=A[mid].v[i];
            root.mi[i]=root.ma[i]=root.v[i];
        }
    root.ls=root.rs=0;
    if(l<=mid-1)
        root.ls=BuildTree(l,mid-1,dimen^1);
    if(mid+1<=r)
        root.rs=BuildTree(mid+1,r,dimen^1);
    pushup(o);
    return o;
}
node querynode;
int ans;//最近距离 
int estimate(int o)//估计o到querynode的距离 
{
    if(!o)
        return inf;
    int res=0;
    for(int i=0;i<2;++i)
        {
            if(querynode.v[i]<root.mi[i])
                res+=root.mi[i]-querynode.v[i];
            else if(querynode.v[i]>root.ma[i])
                res+=querynode.v[i]-root.ma[i];
        }
    return res;
}
void query(int o)
{
    int dist=abs(root.v[0]-querynode.v[0])+abs(root.v[1]-querynode.v[1]);//实际距离 
    ans=min(ans,dist);
    int dl=estimate(root.ls),dr=estimate(root.rs);
    if(dl<dr)
        {
            if(dl<ans)
                query(root.ls);
            if(dr<ans)
                query(root.rs);
        }
    else
        {
            if(dr<ans)
                query(root.rs);
            if(dl<ans)
                query(root.ls);
        }
}
void Insert()//像平衡树一样,比较大小向下走,走到合适的地方接上 
{
    ++n;
    for(int i=0;i<2;++i)
        {
            A[n].v[i]=Tree[n].v[i]=read();
            Tree[n].mi[i]=Tree[n].ma[i]=Tree[n].v[i];
        }
    for(int o=rt,dimen=0;o;dimen^=1)
        {
            for(int i=0;i<2;++i)
                {
                    root.mi[i]=min(root.mi[i],Tree[n].mi[i]);
                    root.ma[i]=max(root.ma[i],Tree[n].ma[i]);
                }
            if(Tree[n].v[dimen]<root.v[dimen])
                {
                    if(root.ls)
                        o=root.ls;
                    else
                        {
                            root.ls=n;
                            return;
                        }
                }
            else
                {
                    if(root.rs)
                        o=root.rs;
                    else
                        {
                            root.rs=n;
                            return;
                        }
                }
        }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;++i)
        for(int j=0;j<2;++j)
            A[i].v[j]=read();
    init();
    rt=BuildTree(1,n,0);
    while(m--)
        {
            int op=read();
            if(op==1)
                {
                    Insert();
                    if(n%K==0)
                        rt=BuildTree(1,n,0);
                }
            else
                {
                    querynode.v[0]=read();
                    querynode.v[1]=read();
                    ans=inf;
                    query(rt);
                    printf("%d\n",ans);
                }
        }
    return 0;
}

转载于:https://www.cnblogs.com/jklover/p/10406843.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值