sgu 128

题意:
在平面上有N个点,现在要求一些线段,使其满足以下要求:
a. 这些线段必须闭合
b. 线段的端点只能是这N个点
c. 交于一点的两条线段成90度角
d. 线段都必须平行于坐标轴
e. 所有线段除在这N个点外不自交
f. 所有线段的长度之和必须最短
如果存在这样的线段,则输出最小长度,否则输出0。
解:a.并查集判断是否是一个闭合图形而不是两个闭合图形 
b.c. d.  可知每一行每一列的坐标上点的数量必须为偶数,若为奇数则肯定会有一个多余的点无法连入图中
f.可推断出若要满足以上条件一定只有一个图存在,所以不用判断是否为最小周长
e.用线段树判断:单点修改,区间查询;

代码如下:

#include<iostream>

#include<algorithm>
#include<vector>
#include<stdio.h>
#define lson (id*2)
#define rson (id*2+1)
using namespace std;
struct edge{
int x;int y;int val;int id;
}tree[80010],p[50005];
vector<int>linx[50010];
vector<int>liny[50010];
int fa[10010];
int find(int a)
{
if (fa[a]==a)
return a;
return fa[a]=find(fa[a]);
}
int unio(int a,int b)
{
fa[find(a)]=find(b);
return 0;
}
int cmp1(edge a,edge b)
{
if (a.x!=b.x)
return a.x<b.x;
else
return a.y<b.y;
}
int cmp2(edge a,edge b)
{
if (a.y!=b.y)
return a.y<b.y;
else
return a.x<b.x;
}

int ans=0;

//开始建树

void push_up(int id)
{
tree[id].val=tree[lson].val+tree[rson].val;
return ;
}
void build_tree(int id,int l,int r)
{
if (l>=r)
{
tree[id].val=0;
return ;
}
int mid=(l+r)/2;
build_tree(lson,l,mid);
build_tree(rson,mid+1,r);
push_up(id);
return ;
}
void add_tree(int id,int l,int r,int x)
{
if (l==r&&r==x)
{
tree[id].val+=1;
tree[id].val%=2;
return ;
}
int mid=(l+r)/2;
if (mid>=x)
add_tree(lson,l,mid,x);
else
add_tree(rson,mid+1,r,x);
push_up(id);
return ;
}
void query_tree(int id,int l,int r,int L,int R)
{
if (L>R) return;
if (L<=l&&R>=r)
{
ans+=tree[id].val;
return ;
}
int mid=(l+r)/2;
if (mid>=L)
query_tree(lson,l,mid,L,R);
if (mid+1<=R)
query_tree(rson,mid+1,r,L,R);
return ;
}//建树完毕
int n,mx=-10004,my=-10004;
long long C=0;
void solve_before()
{
sort(p+1,p+1+n,cmp1);
for (int i=1;i<=n;i++)
linx[p[i].x].push_back(p[i].y);

sort(p+1,p+1+n,cmp2);
for (int i=1;i<=n;i++)
liny[p[i].y].push_back(p[i].x);

sort(p+1,p+1+n,cmp1);
}
int main()
{
build_tree(1,1,20001);
scanf("%d",&n);
for (int i=1;i<=n;i++)
fa[i]=i;
for (int i=1;i<=n;i++)
{
scanf("%d%d",&p[i].x,&p[i].y);
p[i].x+=10001;
p[i].y+=10001;
p[i].id=i;
mx=max(mx,p[i].x);
my=max(my,p[i].y);
}
solve_before();
for (int i=0;i<=max(mx,my);i++)
{
if (linx[i].size()%2!=0||liny[i].size()%2!=0)
{
cout<<"0"<<endl;
return 0;
}
}
for (int i=1;i<=n;i+=2)
{
ans=0;
C+=p[i+1].y-p[i].y;
unio(p[i].id,p[i+1].id);
add_tree(1,1,20001,p[i].y);
add_tree(1,1,20001,p[i+1].y);
query_tree(1,1,20001,p[i].y+1,p[i+1].y-1);
if (ans>0)
{
cout<<"0"<<endl;
return 0;
}

}
sort(p+1,p+1+n,cmp2);

for (int i=1;i<=n;i+=2)
{
unio(p[i+1].id,p[i].id);
C+=p[i+1].x-p[i].x;
}
for (int i=1;i<n;i++)
{
if (find(i)!=find(i+1))
{
cout<<"0"<<endl;
return 0;
}
}
cout<<C<<endl;
return 0;

}

更具体题解参考     : http://blog.sina.com.cn/s/blog_51cea4040100gf9l.html

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值