【BZOJ-2177】曼哈顿最小生成树 Kruskal + 树状数组

2177: 曼哈顿最小生成树

Time Limit: 10 Sec  Memory Limit: 259 MB
Submit: 190  Solved: 77
[Submit][Status][Discuss]

Description

平面坐标系xOy内,给定n个顶点V = (x , y)。对于顶点u、v,u与v之间的距离d定义为|xu – xv| + |yu – yv| 你的任务就是求出这n个顶点的最小生成树。

Input

第一行一个正整数n,表示定点个数。 
接下来n行每行两个正整数x、y,描述一个顶点。 

Output

只有一行,为最小生成树的边的距离和。 

Sample Input

4
1 0
0 1
0 -1
-1 0

Sample Output

6

HINT

对于100%的数据n <= 50000;
0 <= x, y <= 100000。

Source

Solution

曼哈顿距离最小生成树裸题

那么来说一下曼哈顿距离最小生成树

首先给出$N$个点,求普通的最小生成树,Kruskal的时间复杂度是$O(MlogN)$,显然$M$的级别是$N^{2}$的,但曼哈顿距离最小生成树能做到$O(NlogN)$

先考虑最小生成树的一个性质:如果图中存在一个环,那么把环上最大边删掉,得到的与不删环的MST权和是一样的

利用这个性质,我们构建边的时候,就能大大减少边的数量

考虑现在在一个点$S$,可以从这个点为中心,把平面分成8份

然后我们发现,在一个象限中,点$S$至多和一个点相连的边是有用的。

先说一个自己的理解但不是很详尽的证明;

我们考虑,如果全都连边,大概是这样的情况,但是我们发现,$|AC|<|AB| && |CB|<|AB|$

那么考虑环切定理,这样,边AB是可以删去的,所以很容易理解每个点只需要连象限中离他最近的点即可

证明:

八个扇形区域是对称的,我们只考虑R1

把s看作原点,R1里面的点(x,y)都满足:

x≥0,

y>x.

考察R1里面两个点p和q,不失一般性设xp≤xq

1.       yp≤yq

|PQ|=xq+yq-(xp+xq)

|SP|=xp+yp

|SQ|=xq+yq

所以|PQ|=|SQ|-|SP|≤|SQ|

可见当yp≤yq时,|PQ|不是三角形SPQ的最长边。(在曼哈顿距离下的“最长”)

2.       yp>y

0≤xp≤xq≤yq<yp

|PQ|=xq-xp+yp-yq

|SP|=xp+yp

|SQ|=xq+yq

即|PQ|= (yp-xp)+(xq-yq)

因为xq≤yq,所以|PQ|≤yp-xp≤yp≤xp+yp=|SP|

也就是说,当yp>yq时,|PQ|仍然不是三角形SPQ的最长边。(曼哈顿距离意义下的“最长”)

综上,|PQ|无论如何也不可能是三角形SPQ的最长边。即:在环<s, p, q>中,最大边只可能是|SP|和|SQ|。根据“环切”性质,我们把|SP|和|SQ|中的较长边删除即可。

假设R1里面有m个顶点:P1, P2, …, Pm,假设距离s最近的点是Pk,那么只要在S和Pk之间连边即可。

所谓距离s最近的点,实际上就是xk+yk最小的点

Code

#include<iostream> 
#include<cstdio> 
#include<cstring> 
#include<algorithm> 
#include<cmath> 
using namespace std; 
int read() 
{ 
    int x=0,f=1; char ch=getchar(); 
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} 
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} 
    return x*f; 
} 
#define MAXN 100010 
int N; 
struct PointNode{int x,y,id;}P[MAXN]; 
bool cmpP(PointNode A,PointNode B) {return A.x!=B.x? A.x<B.x : A.y<B.y;} 
struct UnionFind 
{ 
    int Fa[MAXN]; 
    void Init() {for (int i=1; i<=N; i++) Fa[i]=i;} 
    int Find(int x) {if (Fa[x]==x) return x; else return Fa[x]=Find(Fa[x]);} 
    bool Merge(int x,int y) {int f1=Find(x),f2=Find(y); if (f1==f2) return 0; Fa[f1]=f2; return 1;} 
}uf; 
int Dis(PointNode A,PointNode B) {return abs(A.x-B.x)+abs(A.y-B.y);} 
struct EdgeNode{int to,val,from;}edge[MAXN<<2]; 
int cnt; 
void AddEdge(int u,int v,int w) {cnt++; edge[cnt].from=u; edge[cnt].to=v; edge[cnt].val=w;} 
bool cmpE(EdgeNode A,EdgeNode B) {return A.val<B.val;} 
int lowbit(int x) {return x&-x;} 
struct BITNode{int minn,pos; void init() {minn=0x7fffffff; pos=-1;}}bit[MAXN]; 
void Update(int x,int val,int pos) 
{ 
    for (int i=x; i; i-=lowbit(i)) 
        if (val<bit[i].minn) bit[i].minn=val,bit[i].pos=pos; 
} 
int Query(int x,int M) 
{ 
    int minn=0x7fffffff,pos=-1; 
    for (int i=x; i<=M; i+=lowbit(i)) 
        if (bit[i].minn<minn) minn=bit[i].minn,pos=bit[i].pos; 
    return pos; 
}
int num,Ans;
void MST() 
{ 
    int a[MAXN],b[MAXN]; 
    for (int k=0; k<=3; k++) 
        { 
            if (k==1 || k==3) for (int i=1; i<=N; i++) swap(P[i].x,P[i].y); 
                else if (k==2) for (int i=1; i<=N; i++) P[i].x=-P[i].x; 
            stable_sort(P+1,P+N+1,cmpP); 
            for (int i=1; i<=N; i++) 
                a[i]=b[i]=P[i].y-P[i].x; 
            stable_sort(b+1,b+N+1); 
            int M=unique(b+1,b+N+1)-b-1; 
            for (int i=1; i<=M; i++) bit[i].init(); 
            for (int i=N; i>=1; i--) 
                { 
                    int pos=lower_bound(b+1,b+M+1,a[i])-b; 
                    int ans=Query(pos,M); 
                    if (ans!=-1) AddEdge(P[i].id,P[ans].id,Dis(P[i],P[ans])); 
                    Update(pos,P[i].x+P[i].y,i); 
                } 
        } 
    stable_sort(edge+1,edge+cnt+1,cmpE); 
    uf.Init(); 
    for (int i=1; i<=cnt; i++) 
        { 
            int u=edge[i].from,v=edge[i].to; 
            if (uf.Merge(u,v)) Ans+=edge[i].val,num++; 
            if (num==N-1) break; 
        } 
} 
int main() 
{ 
    N=read(); 
    for (int i=1; i<=N; i++) P[i].x=read(),P[i].y=read(),P[i].id=i; 
    MST(); 
    printf("%d\n",Ans); 
    return 0; 
}

感觉很愚蠢的代码

转载于:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5795636.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
后台采用apache服务器下的cgi处理c语言做微信小程序后台逻辑的脚本映射。PC端的服务器和客户端都是基于c语言写的。采用mysql数据库进行用户数据和聊天记录的存储。.zip C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值