Description
这天,SJY显得无聊。在家自己玩。在一个棋盘上,有N个黑色棋子。他每次要么放到棋盘上一个黑色棋子,要么放上一个白色棋子,如果是白色棋子,他会找出距离这个白色棋子最近的黑色棋子。此处的距离是 曼哈顿距离 即(|x1-x2|+|y1-y2|) 。现在给出N<=500000个初始棋子。和M<=500000个操作。对于每个白色棋子,输出距离这个白色棋子最近的黑色棋子的距离。同一个格子可能有多个棋子。
Input
第一行两个数 N M
以后M行,每行3个数 t x y
如果t=1 那么放下一个黑色棋子
如果t=2 那么放下一个白色棋子
Output
对于每个T=2 输出一个最小距离
Sample Input
2 3
1 1
2 3
2 1 2
1 3 3
2 4 2
Sample Output
1
2
HINT
kdtree可以过
分析:这是一道k-d tree求最近点对的模版题。详见代码注释。
代码:
/**************************************************************
Problem: 2648
User: beginend
Language: C++
Result: Accepted
Time:12388 ms
Memory:40356 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
const int maxn=5e5+7;
const int kd=2;//维数
using namespace std;
struct rec{
int d[kd];
};//定义一个n维的向量的类型
struct node{
int lc,rc;//左右儿子
int l[kd],r[kd];//每一维的左右边界
int d[kd];//作为根的这个点的n维向量
}t[maxn*2];
rec a[maxn];
int n,m,cnt,root,D,ans;
bool cmp(rec a,rec b)//判断以D维为关键字,D+1维为第二关键字……的cmp
{
return (a.d[D]<b.d[D]) || (a.d[D]==b.d[D]) && (a.d[D^1]<b.d[D^1]);
}
void updata(int x,int y)//更新数据,有点像平衡树,其实k-d tree的维护有点像平衡树
{
for (int i=0;i<kd;i++)
{
t[x].l[i]=min(t[x].l[i],t[y].l[i]);
t[x].r[i]=max(t[x].r[i],t[y].r[i]);
}
}
void build(int &p,int l,int r,int d)//建k-d tree
{
D=d;
if (p==0) p=++cnt; //动态开点
int mid=(l+r)/2;
nth_element(a+l,a+mid,a+r+1,cmp);//意思是把第mid小的数放在第mid个位置,并且[l,mid-1]都小于mid,[mid+1,r]都大于mid
//注意是a+r+1,其他三个系数很像sort的格式
//这步相当于取中造平衡树,和平衡树找中位数很像
for (int i=0;i<kd;i++)//更新向量
{
t[p].d[i]=a[mid].d[i];
t[p].l[i]=t[p].r[i]=t[p].d[i];
}
if (l<mid) build(t[p].lc,l,mid-1,d^1);//换维建树,对于多维应该是(d+1)%kd
if (mid<r) build(t[p].rc,mid+1,r,d^1);
if (t[p].lc) updata(p,t[p].lc);
if (t[p].rc) updata(p,t[p].rc);
}
void insert(int x)//直接插入的平衡树,splay或替罪羊都没有打,偷懒一下
{
int i=root;
D=0;
while (1)
{
updata(i,x);
if (t[x].d[D]<=t[i].d[D])
{
if (!t[i].lc)
{
t[i].lc=x;
return;
}
i=t[i].lc;
}
else
{
if (!t[i].rc)
{
t[i].rc=x;
return;
}
i=t[i].rc;
}
D^=1;
}
}
int g(int p,rec s)//估价函数,此处的估价为当前点到以t[p]为根的点围成的矩形的距离(本题为曼哈顿距离),如果点在矩形内则为0。
{
int ret=0;
for (int i=0;i<kd;i++)
{
ret+=max(t[p].l[i]-s.d[i],0);
ret+=max(s.d[i]-t[p].r[i],0);
}
return ret;
}
int dis(rec a,rec b)//a,b的曼哈顿距离
{
int ret=0;
for (int i=0;i<kd;i++)
{
ret+=abs(a.d[i]-b.d[i]);
}
return ret;
}
void ask(int p,rec s)
{
rec r;
int dl=0x3f3f3f3f,dr=0x3f3f3f3f;
r.d[0]=t[p].d[0];
r.d[1]=t[p].d[1];
ans=min(ans,dis(s,r));计算与根的距离
if (t[p].lc) dl=g(t[p].lc,s);//进行估价
if (t[p].rc) dr=g(t[p].rc,s);
if (dl<dr)//反正先枚举估价小的,即离边界最近的
{
if (dl<ans) ask(t[p].lc,s);
if (dr<ans) ask(t[p].rc,s);
}
else
{
if (dr<ans) ask(t[p].rc,s);
if (dl<ans) ask(t[p].lc,s);
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].d[0],&a[i].d[1]);
}
build(root,1,n,0);
for (int i=1;i<=m;i++)
{
int op;
scanf("%d",&op);
if (op==1)
{
rec s;
scanf("%d%d",&s.d[0],&s.d[1]);
cnt++;
for (int i=0;i<kd;i++)
{
t[cnt].d[i]=s.d[i];
t[cnt].l[i]=t[cnt].r[i]=t[cnt].d[i];
}
insert(cnt);
}
else
{
rec s;
scanf("%d%d",&s.d[0],&s.d[1]);
ans=0x3f3f3f3f;
ask(root,s);
printf("%d\n",ans);
}
}
}