Sgu 128 Snake

题目连接:http://acm.sgu.ru/problem.php?contest=0&problem=128

题意:给出n个点。给这n个点连线,使得满足:

1.这n个点连线后形成的折线是闭合的。

2.折线必须包含所有的n个点,且只能包含这n个点。

3.折线中相邻线段要形成90度的转角。

4.每条线段都必须是平行于坐标轴的,也就是说只能有x方向和y方向的线段。

5.形成的线段不能自交。

6.折线必须具有最小长度。

分析:可以证明:符合前四个条件的折线答案只能有一个。因为可以证明,每一点只能发散出两条线段,且一条是竖线,一条是横线。

而且以平行于x轴的线段为例:(x1,y)、(x2,y)、(x3,y)、(x4,y)、(x5,y)、(x6,y).首先点数必须为偶数,其次,连法只能是x1-x2,x3-x4,x5-x5.

平行于y轴的线段也是如此。

接下来,我们只要判断形成的唯一图形是否是自交的可以了。

我们可以利用线段树来判断。

我们这样做:

依次扫描平行于x周的线段(x1,x2)。如果在(x1,x2-1)这一区间内有点标记存在,那么肯定标记的点会形成一条竖线,并且会穿透(x1,x2-1),也就是会存在相交。

如果x1点没有被访问过,update(1),如果已经被访问过,update(-1), 相当于是一种闭合的相互抵消,便于不影响以后的判断。x2点类似。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;

#define Maxn 20100
#define lx (x<<1)
#define rx ((x<<1) | 1)
#define MID ((l + r)>>1)

struct Point
{
	int x,y;
}p[Maxn];

vector <int> s[Maxn<<2],match[Maxn];
int maxx,ans,vis[Maxn],S[Maxn<<2];

bool cmpy(int a,int b) {return p[a].y<p[b].y;}
bool cmpx(int a,int b) {return p[a].x<p[b].x;}

void init()
{
	maxx = 0;
	ans = 0	;
	memset(vis,0,sizeof(vis));
	memset(S,0,sizeof(S));
	memset(match,0,sizeof(match));
}
void dfs(int i)
{
	vis[i] = 1;
	for(int k=0;k<2;k++)
	{
		if(!vis[match[i][k]]) dfs(match[i][k]);
	}
}
void pushUp(int x)
{
	S[x] = S[lx] + S[rx];
}
void update(int p,int d,int l,int r,int x)
{
	if(l==r)
	{
		S[x] += d;
		return;
	}
	if(p<=MID) update(p,d,l,MID,lx);
    else update(p,d,MID+1,r,rx);
    pushUp(x);
}
int query(int L,int R,int l,int r,int x)
{
	if(L<=l && r<=R)
	{
		return S[x];
	}
	int ans = 0;
	if(L<=MID) ans += query(L,R,l,MID,lx);
	if(MID<R) ans += query(L,R,MID+1,r,rx);
	return ans;
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
	int n;
	scanf(" %d",&n);
	init();
	for(int i=1;i<=n;i++)
	{
		scanf(" %d %d",&p[i].x,&p[i].y);
		p[i].x += 10001,p[i].y += 10001;
		maxx = max(maxx,max(p[i].x,p[i].y));
	}
	//x
	for(int i=1;i<=n;i++) s[p[i].x].push_back(i);

	for(int i=1;i<=maxx;i++)
	{
		if(s[i].size())
		{
			if(s[i].size()&1) goto L1;
			sort(s[i].begin(),s[i].end(),cmpy);
			for(int j=0;j<s[i].size();j+=2)
			{
				ans += p[s[i][j+1]].y - p[s[i][j]].y;
				match[s[i][j+1]].push_back(s[i][j]);
				match[s[i][j]].push_back(s[i][j+1]);
			}
			s[i].clear();
		}
	}

	//y
	for(int i=1;i<=n;i++) s[p[i].y].push_back(i);

	for(int i=1;i<=maxx;i++)
	{
		if(s[i].size())
		{
			if(s[i].size()&1) goto L1;
			sort(s[i].begin(),s[i].end(),cmpx);
			for(int j=0;j<s[i].size();j+=2)
			{
				ans += p[s[i][j+1]].x - p[s[i][j]].x;
				match[s[i][j+1]].push_back(s[i][j]);
				match[s[i][j]].push_back(s[i][j+1]);
			}
			//s[i].clear();
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(match[i].size()!=2) goto L1;
	}
	//连通
	dfs(1);
	for(int i=1;i<=n;i++) if(!vis[i]) goto L1;
	memset(vis,0,sizeof(vis));
	//判断相交
	for(int i=1;i<=maxx;i++)
	{
		if(s[i].size())
		{
			for(int j=0;j<s[i].size();j+=2)
			{
				int x1 = p[s[i][j]].x;
				int x2 = p[s[i][j+1]].x;
				if(x2-x1>1 && (query(1,x2-1,1,maxx,1) - query(1,x1,1,maxx,1))>0)
					goto L1;
				if(!vis[x1]) update(x1,1,1,maxx,1);
				else update(x1,-1,1,maxx,1);
				if(!vis[x2]) update(x2,1,1,maxx,1);
				else update(x2,-1,1,maxx,1);
				vis[x1] = !vis[x1];
				vis[x2] = !vis[x2];
			}
		}
	}
	printf("%d\n",ans);
	return 0;
	L1: puts("0");
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值