bzoj 2648: SJY摆棋子 K-DTree

题意

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

分析

K-DTree模板题,当作复习
每次记录子树内最大最小值,看看距离询问点至少有多远,来写估价函数

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
inline int read()
{
  int p=0; int f=1; char ch = getchar();
  while(ch<'0' || ch>'9'){if(ch == '-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}

struct node
{
  int son[2] , mn[2] , mx[2] , d[2];
}tr[N]; int rt,tot;

int D;
bool cmp(const node &x,const node &y)
{
  if(x.d[D] != y.d[D]) return x.d[D] < y.d[D];
  return x.d[D^1] < y.d[D^1];
}

void upd(int x,int y)
{
  for(int i=0;i<2;i++)
  {
    tr[x].mn[i] = min(tr[x].mn[i],tr[y].mn[i]);
    tr[x].mx[i] = max(tr[x].mx[i],tr[y].mx[i]);
  }
}

int build(int dem,int L,int R)
{
  D = dem; int mid=(L+R)>>1; nth_element(tr+L,tr+mid,tr+R+1,cmp);
  tr[mid].mn[0] = tr[mid].mx[0] = tr[mid].d[0];
  tr[mid].mn[1] = tr[mid].mx[1] = tr[mid].d[1];
  if(L<mid) tr[mid].son[0] = build(dem^1,L,mid-1),upd(mid,tr[mid].son[0]);
  if(mid<R) tr[mid].son[1] = build(dem^1,mid+1,R),upd(mid,tr[mid].son[1]);
  return mid;
}

void insert(int x)
{
  int u = rt; D = 1;
  while(u)
  {
    upd(u,x);
    if(tr[x].d[D] <= tr[u].d[D])
    {
      if(tr[u].son[0]) u=tr[u].son[0];
      else{tr[u].son[0] = x; return ;}
    }
    else
    {
      if(tr[u].son[1]) u=tr[u].son[1];
      else{tr[u].son[1] = x; return ;}
    }
    D^=1;
  }
}

int ans = INT_MAX;

int calc(int u,int x,int y)
{
  int s = 0;
  if(tr[u].mx[0] < x) s += x - tr[u].mx[0];
  if(tr[u].mx[1] < y) s += y - tr[u].mx[1];

  if(tr[u].mn[0] > x) s += tr[u].mn[0] - x;
  if(tr[u].mn[1] > y) s += tr[u].mn[1] - y;
  return s;
}

void qry(int u,int x,int y)
{
  if(!u) return ;
  ans = min(ans , abs(x - tr[u].d[0]) + abs(y - tr[u].d[1]));
  int d0 = INT_MAX,d1 = INT_MAX;
  if(tr[u].son[0]) d0 = calc(tr[u].son[0],x,y); if(tr[u].son[1]) d1 = calc(tr[u].son[1],x,y);
  if(d0 < d1)
  {
    if(d0 < ans) qry(tr[u].son[0],x,y);
    if(d1 < ans) qry(tr[u].son[1],x,y);
  }
  else
  {
    if(d1 < ans) qry(tr[u].son[1],x,y);
    if(d0 < ans) qry(tr[u].son[0],x,y);
  }
}

int main()
{

  int n = read(); int m = read();
  for(int i=1;i<=n;i++) tr[i].d[0] = read(), tr[i].d[1] = read();
  rt = build(1,1,n);

  for(int i=1;i<=m;i++)
  {
    int op = read();
    if(op==1)
    {
      n++; tr[n].d[0] = tr[n].mx[0] = tr[n].mn[0] = read(); tr[n].d[1] = tr[n].mx[1] = tr[n].mn[1] = read();
      insert(n);
    }
    else
    {
      int x = read(); int y = read(); ans=INT_MAX;
      qry(rt,x,y);
      printf("%d\n",ans);
    }
  }

  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值