曼哈顿距离最小生成树
  TEZNKK3IfmPf 10天前 22 0

一个点把平面分成了8个部分:

我们只需要让这个点与每个部分里距它最近的点连边。

拿R1来说吧:

如图,i的R1区域里距i最近的点是j。也就是说,其他点k都有:

xj + yj <= xk + yk

那么k将落在如下阴影部分:

显然,边(i,j), (j,k), (i,k)构成一个环<i,j,k>,而(i,k)一定是最长边,可以被删去。所以我们只连边(i,j)。

为了避免重复加边,我们只考虑R1~R4这4个区域。(总共加了4N条边)

这4个区域的点(x,y)要满足什么条件?

如果点(x,y)在R1,它要满足:x ≥ xi ,y – x ≥ yi – xi(最近点的x + y最小)

如果点(x,y)在R2,它要满足:y ≥ yi ,y – x ≤ yi – xi(最近点的x + y最小)

如果点(x,y)在R3,它要满足:y ≤ yi ,y + x ≥ yi + xi(最近点的y – x最小)

如果点(x,y)在R4,它要满足:x ≥ xi ,y + x ≤ yi – xi(最近点的y – x最小)

其中一个条件用排序,另一个条件用数据结构(这种方法很常用),在数据结构上询问,找最近点。因为询问总是前缀或后缀,所以可以用树状数组。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<bitset>
#include<set>
#include<stack>
#include<map>
#include<list>
#include<new>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pi=acos(-1.0);
const double E=2.718281828459;
const ll mod=1e8+7;
const int INF=0x3f3f3f3f;

int n,k;

struct node{
int x;
int y;
int id;
bool friend operator<(node a,node b){
return a.x==b.x?a.y<b.y:a.x<b.x;
///保证树状数组更新和查询时不会遗漏
}
}point[10005];

struct edge{
int s;
int e;
int c;
bool friend operator<(edge a,edge b){
return a.c<b.c;
}
}load[40000];
int sign=0;
int p[10005];
int find(int x){
return p[x]==x?x:p[x]=find(p[x]);
}

void kruskal(){
for(int i=1;i<=n;i++)
p[i]=i;
sort(load+1,load+1+sign);
int cnt=0;
for(int i=1;i<=sign;i++){
int x=find(load[i].s);
int y=find(load[i].e);
if(x!=y){
cnt++;
p[x]=y;
if(cnt==n-k){
printf("%d\n",load[i].c);
return ;
}
}
}
}

int id[10005]; ///y-x为索引的编号
int xy[10005]; ///y-x为索引 x+y的最小值

void update(int index,int minn,int s) ///index:y-x minn:x+y s:编号
{
index+=1000;
for(int i=index;i>=1;i-=(i&(-i))){
if(xy[i]>minn){
xy[i]=minn;
id[i]=s;
}
}
}

void query(int index,int minn,int s) ///index:y-x minn:x+y s:编号
{
index+=1000;
int e=-1,c=INF;
///现在以编号s为原点,查询y-x>=index的点中x+y的最小值
for(int i=index;i<10000;i+=(i&(-i))){
if(xy[i]<c){
e=id[i];
c=xy[i];
}
}
if(e!=-1)
load[++sign]=edge{s,e,c-minn};
}

void build_edge()
{
/// 以(xi,yi)为原点,对于第1区域内的点(x,y)满足条件
/// x>=xi,y-x>=yi-xi,(x+y)最小
sort(point+1,point+1+n);
memset(id,-1,sizeof(id));
fill(xy,xy+10005,INF);
///按照x升序
///保证后面查询时,x都比当前的x大
for(int i=n;i>=1;i--){
int index=point[i].y-point[i].x;
int minn=point[i].x+point[i].y;
query(index,minn,point[i].id);
update(index,minn,point[i].id);
}
}

int main() ///第K大边
{
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d %d",&point[i].x,&point[i].y);
point[i].id=i;
}
///1象限建边
build_edge();

///2象限建边
for(int i=1;i<=n;i++)
swap(point[i].x,point[i].y);
build_edge();

///3象限建边
for(int i=1;i<=n;i++)
point[i].x=-point[i].x;
build_edge();

///4象限建边
for(int i=1;i<=n;i++)
swap(point[i].x,point[i].y);
build_edge();
kruskal();
return 0;
}

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 10天前 0

暂无评论

推荐阅读
  TEZNKK3IfmPf   10天前   20   0   0 编程开发i++
  TEZNKK3IfmPf   10天前   26   0   0 i++
  TEZNKK3IfmPf   2024年06月14日   50   0   0 i++ico字符串
TEZNKK3IfmPf