[算法模版]曼哈顿距离最小生成树
算法描述
先说结论。做法就是把平面等分成8块。可以证明以(i)为坐标原点的八个区域中,每个区域都只有离(i)最近的点可能和(i)连边。一旦我们确定了这些点,我们就把边数压缩到了(8n),就可以直接跑了。
对于在以(i)为坐标原点(y)轴正半轴右边那个区域(右上角),区域内离他最近的点(j)满足(y_j-x_jgeq y_i-x_i),同时保证(y_j+x_j)最小。
第一个限制保证了在右上角那个区间,第二个保证了距离最近。
我们就可以用数据结构来维护这个东西。(把(y-x)离散一下,维护([y-x,infty])中(y+x)最小的是多少就好了)
对于其他区域最近点的求解,我们可以把它对称到右上角的区域来解决。
证明
下面我们将证明
以(i)为坐标原点的八个区域中,每个区域都只有离(i)最近的点可能和(i)连边
例题
这道题还要用到一个结论:要令两个点之间路径上最大边最小,选择的路径就是最小生成树上路径。(因为如果最大的路径不是最小生成树上的路径一定不优)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#define pr pair<int,int>
#define ll long long
using namespace std;
const int maxn=200500;
#define inf 1061109567
const int fff=(int)2e9+1000;
int n,val[maxn],who[maxn],bk[maxn][2],mem[maxn];
int changex[10]={0,1,1,-1,-1,1,1,-1,-1};
int changey[10]={0,1,-1,-1,1,1,-1,-1,1};
int changec[10]={0,0,0,0,0,1,1,1,1};
struct gg {
int x,y,dif,sum,ox,oy,rk,id;//rk代表离散化之后的排名,id为最初输入的编号
}node[maxn];
struct fuck {
int fi,se;
};
namespace seg {
int mi[maxn<<2],num[maxn<<2];
void push_up(int root) {
mi[root]=min(mi[root<<1],mi[root<<1|1]);
if(mi[root<<1]>mi[root<<1|1]){num[root]=num[root<<1|1];}
else{num[root]=num[root<<1];}
}
void build(int l,int r,int root) {
if(l==r){mi[root]=node[l].sum;num[root]=l;return;}
int mid=(l+r)>>1;
build(l,mid,root<<1);build(mid+1,r,root<<1|1);
push_up(root);
}
void update(int l,int r,int root,int tar) {
if(l==r){mi[root]=mem[who[l]];num[root]=who[l];return;}
int mid=(l+r)>>1;
if(tar<=mid)update(l,mid,root<<1,tar);
else update(mid+1,r,root<<1|1,tar);
push_up(root);
}
fuck qnum(int l,int r,int root,int tl,int tr) {
fuck re={fff,0},tmp;
if(tl>tr)return re;
if(tl<=l&&r<=tr){fuck rea={mi[root],num[root]};return rea;}
int mid=(l+r)>>1;
if(tl<=mid){tmp=qnum(l,mid,root<<1,tl,tr);if(tmp.fi<re.fi)re=tmp;}
if(tr>=mid+1){tmp=qnum(mid+1,r,root<<1|1,tl,tr);if(tmp.fi<re.fi)re=tmp;}
return re;
}
}
namespace tree {
int cnt,head[maxn],fa[maxn],tot,f[maxn][19],len[maxn][19],dep[maxn];//tot为边数
map<pair<int,int>,bool>ex;
struct edge {
int u,v,w,next;
}side[maxn*2*8];
struct data {
int u,v,w;
}con[maxn*8];
void insert(int u,int v,int w) {
struct edge add={u,v,w,head[u]};side[++cnt]=add;head[u]=cnt;
};
int get(int x){
if(fa[x]==x)return x;
fa[x]=get(fa[x]);
return fa[x];
}
void uni(int x,int y) {
fa[get(x)]=get(y);
}
bool cop2(data x,data y){return x.w<y.w;}
void dfs(int now,int g,int w) {
f[now][0]=g;len[now][0]=w;dep[now]=dep[f[now][0]]+1;
for(int i=1;i<=18;i++){f[now][i]=f[f[now][i-1]][i-1];len[now][i]=max(len[now][i-1],len[f[now][i-1]][i-1]);}
for(int i=head[now];i;i=side[i].next) {
int v=side[i].v;if(v==f[now][0]||f[v][0]==now)continue;dfs(v,now,side[i].w);
}
}
void span(){
sort(con+1,con+1+tot,cop2);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=tot;i++) {
int u=con[i].u,v=con[i].v,w=con[i].w;
if(get(u)!=get(v)){
uni(u,v);
insert(u,v,w);insert(v,u,w);
}
}
dfs(1,0,0);
}
int lca(int u,int v) {
int ma=0;
if(dep[u]<dep[v])swap(u,v);
for(int i=18;i>=0;i--)if(dep[f[u][i]]>=dep[v]){ma=max(ma,len[u][i]);u=f[u][i];}
if(u==v)return ma;
for(int i=18;i>=0;i--) {
if(f[u][i]!=f[v][i]) {
ma=max(ma,len[u][i]);ma=max(ma,len[v][i]);
u=f[u][i],v=f[v][i];
}
}
return max(ma,max(len[u][0],len[v][0]));
}
}
inline int get_dis(int x,int y) {
return abs(bk[x][0]-bk[y][0])+abs(bk[x][1]-bk[y][1]);
}
inline bool cop1(gg x,gg y) {
return x.dif<y.dif;
}
inline bool cop3(gg x,gg y) {
if(x.x==y.x) {
return x.y<y.y;
}
return x.x<y.x;
}
inline bool cop4(gg x,gg y) {
return x.id<y.id;
}
void init() {
memset(tree::head,0,sizeof(tree::head));
// xdmemset(seg::mi,0x3f,sizeof(seg::mi));
tree::ex.clear();tree::tot=tree::cnt=0;
memset(tree::f,0,sizeof(tree::f));memset(tree::len,0,sizeof(tree::len));memset(tree::dep,0,sizeof(tree::dep));
}
void solve() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d%d",&node[i].ox,&node[i].oy);node[i].id=i;
}
init();
for(int i=1;i<=8;i++) {//枚举4种变换
memset(seg::mi,0x3f,sizeof(seg::mi));
//memset(seg::num,0,sizeof(seg::num));
//sort(node+1,node+1+n,cop4);
for(int j=1;j<=n;j++) {
node[j].x = node[j].ox * changex[i];
node[j].y = node[j].oy * changey[i];
if(changec[i])swap(node[j].x,node[j].y);
bk[node[j].id][0]=node[j].x,bk[node[j].id][1]=node[j].y;
node[j].dif = node[j].y - node[j].x;
node[j].sum = node[j].x + node[j].y;mem[node[j].id]=node[j].sum;
}
sort(node+1,node+1+n,cop1);//val:dif第j名dif是多少,who:dif第j名id是多少
for(int j=1;j<=n;j++){node[j].rk=j;val[j]=node[j].dif;who[j]=node[j].id;}
// seg::build(1,n,1);
sort(node+1,node+1+n,cop3);
for(int j=n;j>=1;j--) {//枚举哪个点为坐标原点
seg::update(1,n,1,node[j].rk);
int l=lower_bound(val+1,val+1+n,node[j].dif)-val;
fuck point1=seg::qnum(1,n,1,l,node[j].rk-1);//查询到一个最小值,返回的second是那个点的编号
fuck point2=seg::qnum(1,n,1,node[j].rk+1,n);
fuck point;
if(point1.fi<point2.fi)point=point1;
else point=point2;
int u=point.se,v=node[j].id;if(!u||!v||point.fi==inf)continue;
if(u>v)swap(u,v);
if(u==v)continue;
//tree::ex[make_pair(u,v)]=1;
//cerr<<u<<' '<<v<<endl;
//tree::insert(u,v,get_dis(point.second,j));
//tree::insert(v,u,get_dis(point.second,j));
tree::con[++tree::tot].u=u;tree::con[tree::tot].v=v;tree::con[tree::tot].w=get_dis(point.se,node[j].id);
}
}
tree::span();//跑一颗生成树
int q;scanf("%d",&q);
for(int i=1;i<=q;i++){
int u,v;scanf("%d%d",&u,&v);
printf("%d
",tree::lca(u,v));
}
}
int main() {
int t;scanf("%d",&t);
while(t--){
solve();
}
}