二维曼哈顿最小生成树



所以我们只要求一个点在其45°角的区域内离他最近的点就行了,而这可以用线段树或树状数组解决

我们以y轴正半轴往右偏45°角的区域为例:

点j在点i的这个区域要满足的条件是:

yj-xj>yi-xi

且xj>xi

那么我们将点以x为第一关键字,y为第二关键字,排序后倒序插入线段树

线段树的线段这一维是离散后的y-x,值是y+x

我们要求的是大于yi-xi的最小的y+x,而xj>xi这个条件已经由插入顺序满足了

这样我们成功的解决了这个区域的点

而其他区域的点我们可以通过坐标变换转移到这个区域

由于对称性,我们注意到其实只要求x轴或y轴正半轴所在的四个区域就行了

那么这个问题就这样解决了


#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <memory>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <climits>
#include <cstring>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <functional>

//#define FIN freopen("input.txt","r",stdin);
//#define FOUT freopen("output.txt","w+",stdout);
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double eps=1e-8;
const double Pi=acos(-1.0);
const int N=50010;

struct point
{
    int x,y,id;
    bool operator<(const point p)const
    {
        return x!=p.x?x<p.x:y<p.y;
    }
} p[N];
struct BIT
{
    int min_val,pos;
    void init()
    {
        min_val=INF;
        pos=-1;
    }
} bit[N];
int par[N];//并查集中父亲
int hight[N];//并查集树的高度
struct edge
{
    int u,v,cost;
};
edge G[N<<2];//边集(边数)
int V,E;//顶点数和边数
int get_Manhadm_dis(point a,point b)
{
    return abs(a.x-b.x)+abs(a.y-b.y);
}
void addedge(int u,int v,int w)
{
    G[E].u=u;
    G[E].v=v;
    G[E++].cost=w;
}
int lowbit(int x)
{
    return x&(-x);
}
void update(int x,int val,int pos)
{
    for(int i=x; i>=1; i-=lowbit(i))
        if(val<bit[i].min_val)
        {
            bit[i].min_val=val;
            bit[i].pos=pos;
        }
}
int ask(int x,int m)
{
    int min_val=INF;
    int pos=-1;
    for(int i=x; i<=m; i+=lowbit(i))
        if(bit[i].min_val<min_val)
        {
            min_val=bit[i].min_val;
            pos=bit[i].pos;
        }
    return pos;
}
void make_edge()
{
    int a[N],b[N];
    for(int dir=0; dir<4; dir++)
    {
        if(dir==1||dir==3)
            for(int i=0; i<V; i++)
                swap(p[i].x,p[i].y);
        else if(dir==2)
            for(int i=0; i<V; i++)
                p[i].x=-p[i].x;
        sort(p,p+V);
        for(int i=0; i<V; i++)
            a[i]=b[i]=p[i].y-p[i].x;
        sort(b,b+V);
        int m=unique(b,b+V)-b;
        for(int i=1; i<=m; i++)
            bit[i].init();
        for(int i=V-1;i>=0; i--)
        {
            int pos=lower_bound(b,b+m,a[i])-b+1;
            int ans=ask(pos,m);
            if(ans!=-1)
                addedge(p[i].id,p[ans].id,get_Manhadm_dis(p[i],p[ans]));
            update(pos,p[i].x+p[i].y,i);
        }
    }
}
//并查集初始化
void Init_union_find(int n)
{
    for(int i=0; i<n; i++)
    {
        par[i]=i;
        hight[i]=0;
    }
}
//查询树的根
int find(int x)
{
    if(par[x]==x)
        return x;
    else
        return par[x]=find(par[x]);
}
//合并x和y所属的集合
void unite(int x,int y)
{
    x=find(x);
    y=find(y);
    if(x==y)
        return ;
    if(hight[x]<hight[y])
        par[x]=y;
    else
    {
        par[y]=x;
        if(hight[x]==hight[y])
            hight[x]++;
    }
}
//判断x和y是否属于同一个集合
bool same(int x,int y)
{
    return find(x)==find(y);
}
bool cmp(const edge& a,const edge& b)
{
    return a.cost<b.cost;
}
int kruskal()
{
    sort(G,G+E,cmp);//按照edge.cost的顺序从小到大排列
    Init_union_find(V);//并查集初始化
    int ans=0;
    for(int i=0; i<E; i++)
    {
        edge e=G[i];
        if(!same(e.u,e.v))
        {
            unite(e.u,e.v);
            ans+=e.cost;
        }
    }
    return ans;
}
int main()
{
    scanf("%d",&V);
    for(int i=0; i<V; i++)
    {
        scanf("%d %d",&p[i].x,&p[i].y);
        p[i].id=i+1;
    }
    E=0;
    make_edge();
    printf("%d\n",kruskal());
}



posted @ 2017-11-04 20:19  Bryce1010  阅读(98)  评论(0编辑  收藏  举报