SGU 128 Snake(线段树)

题意:平面上有n个点,现在要把这些点连成若干线段,满足:

          1、线段都必须闭合

          2、线段的端点必须是给定的点

           3、交与一点的两个线段夹角为90度

            4、所有线段都平行于坐标轴

            5、线段在非端点处不自交

            6、所有线段的长度之和最短

          如果有解,输出最短长度,否则输出0.

思路:这题纠结了很久,最后各种re,把数组开大了,发现TLE……后来实在没办法把数组加了1000,居然神奇的过了……感觉非常不科学,主要问题出在event数组,但是我算的2n就足够了……不知道这个数据怎么出的Orz……这题思路可以参考2004年林涛的集训队论文,写的很详细,首先可以推出的是线段的连接方式是唯一的,剩下就是判断是否闭合和是否自交,是否闭合可以直接搜一遍,然后把y轴离散化以后用线段树判断是否自交就行了。感觉写的有点渣……


代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=10000+1000;
struct Point
{
    int x,y,id;
}p[maxn];
struct Event
{
    int type,v;
    Point d;
    bool operator <(const Event & a) const
    {
        if(d.x!=a.d.x) return d.x<a.d.x;
        if(type==a.type)
        {
            if(type==0) return v;
            return true;
        }
        if(type==0) return v;
        return !a.v;
    }
}event[maxn<<1];
bool pcmp1(Point a,Point b)
{
    return (a.x==b.x&&a.y<b.y)||(a.x<b.x);
}
bool pcmp2(Point a,Point b)
{
    return (a.y==b.y&&a.x<b.x)||(a.y<b.y);
}
bool pcmp3(Point a,Point b)
{
    return a.id<b.id;
}
int Len(Point a,Point b)
{
    return abs(a.x-b.x+a.y-b.y);
}
int sum[maxn<<2];
void PushUp(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
    sum[rt]=0;
    if(l==r) return ;
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
}
int Query(int L,int R,int l,int r,int rt)
{
    if(l>=L&&r<=R) return sum[rt];
    int m=(l+r)>>1;
    int tmp=0;
    if(m>=L) tmp+=Query(L,R,l,m,rt<<1);
    if(m<R) tmp+=Query(L,R,m+1,r,rt<<1|1);
    return tmp;
}
void Update(int p,int l,int r,int rt,int v)
{
    if(l==r)
    {
        sum[rt]+=v;
        return ;
    }
    int m=(l+r)>>1;
    if(m>=p) Update(p,l,m,rt<<1,v);
    else Update(p,m+1,r,rt<<1|1,v);
    PushUp(rt);
}
int con[maxn][2];
bool vis[maxn];
void dfs(int u)
{
    vis[u]=true;
    int v=con[u][0];
    if(v!=-1&&!vis[v]) return dfs(v);
    v=con[u][1];
    if(v!=-1&&!vis[v]) return dfs(v);
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n,m,N;
    scanf("%d",&n);
    for(int i=0;i<n;++i)
    {
        scanf("%d%d",&p[i].x,&p[i].y);
        p[i].id=i;
    }
    memset(vis,0,sizeof(vis));
    memset(con,0xff,sizeof(con));
    int lastv=-inf;
    N=1;m=0;
    ll sum=0;
    bool flag=true;

    sort(p,p+n,pcmp1);
    for(int i=0;i<n;i+=2)
        sum+=Len(p[i],p[i+1]);

    sort(p,p+n,pcmp2);
    for(int i=0;i<n;++i)
    {
        if(p[i].y!=lastv)
        {
            lastv=p[i].y;
            p[i].y=++N;
        }
        else p[i].y=N;
    }
    for(int i=0;i<n;i+=2)
    {
        if(i+1>=n||p[i].y!=p[i+1].y) {flag=false;break;}
        if(p[i].x==p[i+1].x) {flag=false;break;}
        event[m].d=p[i];event[m].type=0;event[m++].v=0;
        event[m].d=p[i+1];event[m].type=0;event[m++].v=1;
        con[p[i].id][0]=p[i+1].id;
        con[p[i+1].id][0]=p[i].id;
        sum+=Len(p[i],p[i+1]);
    }
    sort(p,p+n,pcmp1);
    for(int i=0;i<n;i+=2)
    {
        if(i+1>=n||p[i].x!=p[i+1].x) {flag=false;break;}
        event[m].d=p[i];event[m].type=1;event[m++].v=p[i+1].y;
        con[p[i].id][1]=p[i+1].id;
        con[p[i+1].id][1]=p[i].id;
    }
    if(flag)
    {
        sort(p,p+n,pcmp3);
        dfs(0);
        for(int i=0;i<n;++i)
            if(!vis[i]) {flag=false;break;}
    }
    if(flag)
    {
        sort(event,event+m);
        build(1,N,1);
        int L,R,tmp;
        for(int i=0;i<m;++i)
        {
            if(event[i].type==0)
            {
                if(event[i].v==0) Update(event[i].d.y,1,N,1,1);
                else Update(event[i].d.y,1,N,1,-1);
            }
            else
            {
                L=event[i].d.y;
                R=event[i].v;
                L++;R--;
                if(L<=R)
                {
                    tmp=Query(L,R,1,N,1);
                    if(tmp) {flag=false;break;}
                }
            }
        }
    }
    if(flag) printf("%lld\n",sum);
    else printf("0\n");
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值