• 性质:每个点向坐标系八个方向最近的点连边
  • 实现:如y轴右偏45°区域,满足\(x_0<=x_1,y_0<=y_1\)\(y_1-x_1>=y_0-x_0\)
    因此\(x_1-x_0+y_1-y_0=(x_1+y_1)-(x_0+y_0)\),用线段树维护下标为\(y_1-x_1\),值\(x_1+y_1\)
    每次修改前,查询下标大于\(y_0-x_0\),值尽量小
    最后跑kruskar即可
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long ll;
int n,c[N],p[N],ad=2e5,ecnt,inf=0x3f3f3f3f,fa[N];
int g_fa(int u) {
	if(fa[u]==u) return u;
	return fa[u]=g_fa(fa[u]);
}
struct edge {
	int x,y; ll z;
}E[N];
struct node {
	int x,y,id;
}a[N];
bool cmp(node u,node v) {
	if(u.x==v.x) return u.y<v.y;
	return u.x<v.x;
}
bool cmp2(edge u,edge v) {return u.z<v.z;}
int lowbit(int i) {return i&(-i);}
void Update(int del,int sum,int s) {
	del+=ad;
	for(int i=del;i;i-=lowbit(i)) {
		if(c[i]>sum) {
			c[i]=sum,p[i]=s;
		}
	}
}
void query(int del,int sum,int s) {
	del+=ad;
	int pp,minn=inf;
	for(int i=del;i<=ad*2;i+=lowbit(i)) {
		if(c[i]<minn) {
			minn=c[i];
			pp=p[i];
		}
	}
	if(minn!=inf) E[++ecnt]=(edge){s,pp,minn-sum};
}
void Build() {
	memset(c,0x3f,sizeof(c));
	sort(a+1,a+1+n,cmp);
	for(int i=n;i>=1;i--) {
		int u=a[i].y-a[i].x,v=a[i].y+a[i].x;
		query(u,v,a[i].id);
		Update(u,v,a[i].id);
	}
}
void init() {
	Build();
	for(int i=1;i<=n;i++) swap(a[i].x,a[i].y);
	Build();
	for(int i=1;i<=n;i++) a[i].x*=-1;
	Build();
	for(int i=1;i<=n;i++) swap(a[i].x,a[i].y);
	Build();
	for(int i=1;i<=n;i++) fa[i]=i;
}
ll solve() {
	sort(E+1,E+1+ecnt,cmp2);
	ll ans=0;
	for(int i=1,j=1;i<=ecnt&&j<n;i++) {
		int u=g_fa(E[i].x),v=g_fa(E[i].y);
		if(u!=v) {
			fa[u]=v;
			ans+=E[i].z;
			j++;
		}
	}
	return ans;	
}
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%d%d",&a[i].x,&a[i].y); a[i].id=i;
	}
	init();
	ll ans=solve();
	printf("%lld\n",ans);
	return 0;
}