刚学kd-tree,具体的操作网上都有,我就不赘述了。今天暂时只刷了一些曼哈顿距离的题目……都水水的,核心思想是记录出每个超平面(这个词好喜感=v=)的边缘坐标,利用估价函数判断询问点到两边超平面最近的距离是什么,然后先去那个超平面搜索,以达到启发式搜索和剪枝的目的。
具体来说,若要询问绿色的点,它到左边超平面最近的距离就是绿线的距离,我们就把这个距离作为估价函数就好了。(到右边的我没有画出来,但是估价函数会计算出0)
BZOJ2716 & 2648
双倍经验题+模板题,都是裸的询问离某个点曼哈顿距离最近的点是哪个点,直接用我上面说的算法就好了,询问的时候启发式搜索。
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 500000+5
#define INF 0x3f3f3f3f
using namespace std;
int n,m,dim,ans,root,cnt;
struct Point{
int x[2];
Point(int a=0,int b=0){ x[0]=a; x[1]=b; }
inline int& operator [](int i){ return x[i]; }
inline bool operator <(const Point &T)const{ return x[dim]<T.x[dim]; }
}p[maxn];
struct KD_Tree{
int l,r;
int mx[2],mi[2]; //记录边界,使用估价函数剪枝,需要维护一个子树的平面空间范围
Point t;
}tr[maxn<<1];
#define mid ((l+r)>>1)
inline int in(){
int x=0,f=1; char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
for(; isdigit(ch);ch=getchar()) x=x*10+ch-'0';
return x*f;
}
inline void Update_mx(int &x,int y){ if(y>x) x=y; }
inline void Update_mi(int &x,int y){ if(y<x) x=y; }
inline int dis(Point a,Point b){
return abs(a[0]-b[0])+abs(a[1]-b[1]);
}
int calc(int k,Point x){
int res=0;
for(int i=0;i<2;i++){
res+=max(0,tr[k].mi[i]-x[i]);
res+=max(0,x[i]-tr[k].mx[i]);
}
return res;
}
void Update(int k){
for(int i=0;i<2;i++){
if(tr[k].l) Update_mx(tr[k].mx[i],tr[tr[k].l].mx[i]),Update_mi(tr[k].mi[i],tr[tr[k].l].mi[i]);
if(tr[k].r) Update_mx(tr[k].mx[i],tr[tr[k].r].mx[i]),Update_mi(tr[k].mi[i],tr[tr[k].r].mi[i]);
}
}
void Build(int &k,int l,int r,int d){
dim=d; k=++cnt;
nth_element(p+l,p+mid,p+r+1);
tr[k].t=p[mid];
for(int i=0;i<2;i++)
tr[k].mx[i]=tr[k].mi[i]=p[mid][i];
if(l<mid) Build(tr[k].l,l,mid-1,d^1);
if(mid<r) Build(tr[k].r,mid+1,r,d^1);
Update(k);
}
void Insert(int &k,Point pt,int d){
if(!k){
k=++cnt;
for(int i=0;i<2;i++) tr[k].mx[i]=tr[k].mi[i]=pt[i];
tr[k].t=pt; return;
}
dim=d;
if(pt<tr[k].t) Insert(tr[k].l,pt,d^1);
else Insert(tr[k].r,pt,d^1);
Update(k);
}
void Query(int k,Point pt,int d){
if(!k) return;
ans=min(ans,dis(pt,tr[k].t));
int lv=INF,rv=INF;
if(tr[k].l) lv=calc(tr[k].l,pt);
if(tr[k].r) rv=calc(tr[k].r,pt);
if(lv<rv){
if(lv<ans) Query(tr[k].l,pt,d^1);
if(rv<ans) Query(tr[k].r,pt,d^1);
}
else{
if(rv<ans) Query(tr[k].r,pt,d^1);
if(lv<ans) Query(tr[k].l,pt,d^1);
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("2648.in","r",stdin);
freopen("2648.out","w",stdout);
#endif
n=in(); m=in();
for(int i=1;i<=n;i++){
p[i][0]=in(); p[i][1]=in();
}
Build(root,1,n,0);
for(int opt,x,y,i=1;i<=m;i++){
opt=in(); x=in(); y=in();
if(opt==1) Insert(root,Point(x,y),0);
else{
ans=INF;
Query(root,Point(x,y),0);
printf("%d\n",ans);
}
}
return 0;
}
BZOJ1941
跟上面一样是模板题……只需要枚举每个点进行询问,更新最近最远点之间的差就好了。
然而这题要还询问最远点,怎么办呢?
仿照最小值的估价函数也造一个差不多的就好了呗……意义是离某个超平面可能达到的最远距离。
(我会告诉你我偷懒从上面复制代码改了改吗?)
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 500000+5
#define INF 0x3f3f3f3f
using namespace std;
int n,m,dim,ans,ans1,ans2,root,cnt;
struct Point{
int x[2];
Point(int a=0,int b=0){ x[0]=a; x[1]=b; }
inline int& operator [](int i){ return x[i]; }
inline bool operator <(const Point &T)const{ return x[dim]<T.x[dim]; }
}p[maxn];
struct KD_Tree{
int l,r;
int mx[2],mi[2];
Point t;
}tr[maxn<<1];
#define mid ((l+r)>>1)
inline int in(){
int x=0,f=1; char ch=getchar();
for(;!isdigit(ch