基本是参照PKKJ写的   Orz

/*
    题意:给出n<=100000个点坐标,求MST。边权为点之间的曼哈顿距离
    题目给出重要提示:对于一个点O,在45度角度的范围内,最多只有一条连出去的边。 

    拓展这个提示,对于一个点O,45°的范围内最多只有1条边,8个方向只有8条
    8n条边用kruskal可以解决
    由于是无向图,只需要找到4个方向即可。通过旋转90°,关于y=x翻转做到这四个方向区域
    关于原点对称的区域没有访问过。
    那怎么快速找到45°范围内距离(xi,yi)最小的点呢?
    由于是45°范围的点,有xj>xi,yj>yi,距离为xj+yj-xi-yi  所以用线段树找x+y最小的点
    而且需要是在45°范围内,还有y>yi
    先按照w=y-x从小到大排序,w相同的y大的优先
    检查每个点,在线段树中找到距离最小的点(没有时为INF)
    然后再插入
*/

#include
<cstdio>
#include
<cstring>
#include
<vector>
#include
<algorithm>

using namespace std;

const int MAXN = 100010;
const int INF = 1000000000;

struct Edge
{
    
int u,v,w;
    
bool operator<(const Edge &B)const
    
{
        
return w<B.w;
    }

}
;

struct Point
{
    
int x,y;
    Point()
{}
    Point(
int x,int y):x(x),y(y){}
}
;

struct SegTree
{
    
int left,right;
    
int Min,id;
}
;

Point pt[MAXN];
Edge edge[MAXN
*10],*pE;
SegTree T[MAXN
*3];
int fa[MAXN];
int yy[MAXN],ny[MAXN],idx[MAXN];

inline 
int dist(Point A,Point B)
{
    
return abs(A.x-B.x)+abs(A.y-B.y);
}


bool cmp(const int &a,const int &b)
{
    
int w1=pt[a].y-pt[a].x;
    
int w2=pt[b].y-pt[b].x;
    
if(w1==w2)return pt[a].y>pt[b].y;
    
return w1<w2;
}


void build(int p,int left,int right)
{
    T[p].left
=left;
    T[p].right
=right;
    T[p].Min
=INF;
    T[p].id
=-1;
    
if(left==right)return;
    
int mid=(left+right)>>1;
    build(p
<<1,left,mid);
    build((p
<<1)|1,mid+1,right);
}


pair
<int,int> query(int p,int y)
{
    
if(T[p].right<y)return pair<int,int>(INF,-1);
    
if(y<=T[p].left)return pair<int,int>(T[p].Min,T[p].id);
    
return min(query(p<<1,y),query((p<<1)|1,y));
}


void insert(int p,int y,int val,int id)
{
    
if(y<T[p].left||T[p].right<y)return;
    
if(val<T[p].Min)T[p].Min=val,T[p].id=id;//边插入边更新
    if(T[p].left==T[p].right)return;//
    insert(p<<1,y,val,id);
    insert((p
<<1)|1,y,val,id);
}


void add(int u,int v,int w)
{
    pE
->u=u;
    pE
->v=v;
    pE
->w=w;
    pE
++;
}

void make(int n)
{
    
for(int i=0;i<n;i++)
    
{
        idx[i]
=i;
        yy[i]
=pt[i].y;
    }

    sort(idx,idx
+n,cmp);//对idx[]排序,这样才不会破坏原来的pt[]
    sort(yy,yy+n);
    
int nn=unique(yy,yy+n)-yy;
    
for(int i=0;i<n;i++)
    
{
        ny[i]
=lower_bound(yy,yy+nn,pt[idx[i]].y)-yy;
    }

    build(
1,0,nn-1);//
    for(int i=0;i<n;i++)
    
{
        pair
<int,int> res = query(1,ny[i]);//
        if(res.second!=-1)add(idx[i],idx[res.second],dist(pt[idx[i]],pt[idx[res.second]]));
        insert(
1,ny[i],(pt[idx[i]].x+pt[idx[i]].y),i);
    }

}

void solve(int n)
{
    pE
=edge;
    
for(int k=0;k<4;k++)//坐标转换  旋转90; 旋转90+关于y=x翻转; 旋转90
    {                //这样得到的4个区域其关于原点的对称的地方都是没访问过的,不会重复
        if(k>0)
        
{
            
for(int i=0;i<n;i++)
            
{
                
int x=-pt[i].y,y=pt[i].x;
                
if(k==2)swap(x,y);
                pt[i]
=Point(x,y);
            }

        }

        make(n);
    }

}


//kruskal
int find(int a)
{
    
if(a!=fa[a])fa[a]=find(fa[a]);
    
return fa[a];
}

void unin(int a,int b)
{
    a
=find(a),b=find(b);
    
if(a==b)return;
    fa[a]
=b;
}

long long kruskal(int n)
{
    
for(int i=0;i<n;i++)
        fa[i]
=i;
    sort(edge,pE);
    
long long ans = 0;
    
int k=0;
    
for(Edge *p=edge;p!=pE&&k<n-1;p++)
    
{
        
if(find(p->u)!=find(p->v))
        
{
            unin(p
->u,p->v);
            ans
+=p->w;
            k
++;
        }

    }

    
return ans;
}

int main()
{
    
//freopen("in","r",stdin);
    int n,t=1;
    
while(scanf("%d",&n),n)
    
{
        
for(int i=0;i<n;i++)
            scanf(
"%d%d",&pt[i].x,&pt[i].y);
        solve(n);
        printf(
"Case %d: Total Weight = %lld\n",t++,kruskal(n));
    }

    
return 0;
}