bzoj2648&2716 SJY摆棋子 (Kd-Tree)

83 篇文章 0 订阅
7 篇文章 0 订阅

bzoj2648&2716 SJY摆棋子

原题地址
http://www.lydsy.com/JudgeOnline/problem.php?id=2648
http://www.lydsy.com/JudgeOnline/problem.php?id=2716

题意:
在一个棋盘上,有N个黑色棋子。他每次要么放到棋盘上一个黑色棋子,要么放上一个白色棋子,如果是白色棋子,他会找出距离这个白色棋子最近的黑色棋子。此处的距离是 曼哈顿距离 即(|x1-x2|+|y1-y2|) 。现在给出N<=500000个初始棋子。和M<=500000个操作。对于每个白色棋子,输出距离这个白色棋子最近的黑色棋子的距离。同一个格子可能有多个棋子。

数据范围
kdtree可以过。

题解:
裸的Kd-Tree。
像平衡树一样的插入。
我没有重构。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1000006;
int n,m,cur,tail=0,ans,root;
struct node
{
    int ls,rs,d[2],mx[2],mn[2];
}tr[N],t;
bool cmp(const node &A,const node &B){return A.d[cur]<B.d[cur];}
void update(int nd)
{
    int ls=tr[nd].ls; int rs=tr[nd].rs;
    if(ls) 
    {
        tr[nd].mx[0]=max(tr[nd].mx[0],tr[ls].mx[0]);
        tr[nd].mn[0]=min(tr[nd].mn[0],tr[ls].mn[0]);
        tr[nd].mx[1]=max(tr[nd].mx[1],tr[ls].mx[1]);
        tr[nd].mn[1]=min(tr[nd].mn[1],tr[ls].mn[1]);
    }
    if(rs) 
    {
        tr[nd].mx[0]=max(tr[nd].mx[0],tr[rs].mx[0]);
        tr[nd].mn[0]=min(tr[nd].mn[0],tr[rs].mn[0]);
        tr[nd].mx[1]=max(tr[nd].mx[1],tr[rs].mx[1]);
        tr[nd].mn[1]=min(tr[nd].mn[1],tr[rs].mn[1]);    
    }
}
void build(int &nd,int lf,int rg,int now)
{
    int mid=(lf+rg)>>1; nd=mid; cur=now;
    nth_element(tr+lf,tr+mid,tr+rg+1,cmp);
    tr[nd].mx[0]=tr[nd].mn[0]=tr[nd].d[0];
    tr[nd].mx[1]=tr[nd].mn[1]=tr[nd].d[1];
    if(lf<mid) build(tr[nd].ls,lf,mid-1,now^1);
    if(rg>mid) build(tr[nd].rs,mid+1,rg,now^1);
    update(nd);
}
void insert(int nd,int now)
{
    cur=now;
    if(cmp(t,tr[nd])) 
    {
        if(tr[nd].ls) insert(tr[nd].ls,now^1);
        else {tr[nd].ls=++tail; tr[tail]=t;}
    }
    else 
    {
        if(tr[nd].rs) insert(tr[nd].rs,now^1);
        else { tr[nd].rs=++tail; tr[tail]=t;}
    }
    update(nd);
}
int abs(int x){return x<0?-x:x;}
int dis(node A,node B) {return abs(A.d[0]-B.d[0])+abs(A.d[1]-B.d[1]);}
int cal(node &x)
{
    int ret=0;
    for(int i=0;i<2;i++)
    {
        if(t.d[i]>x.mx[i]) ret+=t.d[i]-x.mx[i];
        if(t.d[i]<x.mn[i]) ret+=x.mn[i]-t.d[i];
    }
    return ret;
}
void query(int nd)
{
    ans=min(ans,dis(tr[nd],t)); int dl,dr;
    if(tr[nd].ls) dl=cal(tr[tr[nd].ls]); else dl=inf;
    if(tr[nd].rs) dr=cal(tr[tr[nd].rs]); else dr=inf;
    if(dl<dr)
    {
        if(dl<ans) query(tr[nd].ls);
        if(dr<ans) query(tr[nd].rs);
    }
    else
    {
        if(dr<ans) query(tr[nd].rs);
        if(dl<ans) query(tr[nd].ls);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d%d",&tr[i].d[0],&tr[i].d[1]);
    build(root,1,n,0); tail=n;
    while(m--)
    {
        int opt; scanf("%d%d%d",&opt,&t.d[0],&t.d[1]);
        if(opt==1)
        {
            for(int i=0;i<2;i++) t.mx[i]=t.mn[i]=t.d[i];
            insert(root,0);
        }
        else {ans=inf; query(root); printf("%d\n",ans);}
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值