POJ 2374

Fence Obstacle Course
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 1973 Accepted: 715

Description

Farmer John has constructed an obstacle course for the cows' enjoyment. The course consists of a sequence of N fences (1 <= N <= 50,000) of varying lengths, each parallel to the x axis. Fence i's y coordinate is i. 

The door to FJ's barn is at the origin (marked '*' below). The starting point of the course lies at coordinate (S,N). 
   +-S-+-+-+        (fence #N)

 +-+-+-+            (fence #N-1)

     ...               ...

   +-+-+-+          (fence #2)

     +-+-+-+        (fence #1)

=|=|=|=*=|=|=|      (barn)

-3-2-1 0 1 2 3    

FJ's original intention was for the cows to jump over the fences, but cows are much more comfortable keeping all four hooves on the ground. Thus, they will walk along the fence and, when the fence ends, they will turn towards the x axis and continue walking in a straight line until they hit another fence segment or the side of the barn. Then they decide to go left or right until they reach the end of the fence segment, and so on, until they finally reach the side of the barn and then, potentially after a short walk, the ending point. 

Naturally, the cows want to walk as little as possible. Find the minimum distance the cows have to travel back and forth to get from the starting point to the door of the barn.

Input

* Line 1: Two space-separated integers: N and S (-100,000 <= S <= 100,000) 

* Lines 2..N+1: Each line contains two space-separated integers: A_i and B_i (-100,000 <= A_i < B_i <= 100,000), the starting and ending x-coordinates of fence segment i. Line 2 describes fence #1; line 3 describes fence #2; and so on. The starting position will satisfy A_N <= S <= B_N. Note that the fences will be traversed in reverse order of the input sequence.

Output

* Line 1: The minimum distance back and forth in the x direction required to get from the starting point to the ending point by walking around the fences. The distance in the y direction is not counted, since it is always precisely N.

Sample Input

4 0 
-2 1
-1 2
-3 0
-2 1

Sample Output

4

Hint

This problem has huge input data,use scanf() instead of cin to read data to avoid time limit exceed. 

INPUT DETAILS: 

Four segments like this: 
   +-+-S-+             Fence 4

 +-+-+-+               Fence 3

     +-+-+-+           Fence 2

   +-+-+-+             Fence 1

 |=|=|=*=|=|=|         Barn

-3-2-1 0 1 2 3      

OUTPUT DETAILS: 

Walk positive one unit (to 1,4), then head toward the barn, trivially going around fence 3. Walk positive one more unit (to 2,2), then walk to the side of the barn. Walk two more units toward the origin for a total of 4 units of back-and-forth walking.

Source


我的解法是 线段树优化建图+单源最短路

        假设起点是s,终点是t,建图思想是把每个线段的端点看成节点,如果能从端点i落到一条线段j上,设j的两个断点是jl和jr,那么就建一条从i到jl权值为ABS(i坐标-jl坐标)的边和从i到jr权值为ABS(i坐标-jr坐标)的边,如果从i点跳下可以直接到底那么就向终点t建边,权为ABS(i的坐标)。
         
        但是暴力建图会超时,所以建图的时候需要优化一下,考虑到建图和区间有关,就可以想到使用线段树。对于一个端点i,我们只关心第一个出现的线段j满足jl<i<jr,这样从i落下才可以到线段j上。我的做法是先把s点染色,再按顺序考虑线段,对于每一个线段,先查询区间(l,r)上是否存在已经染色的点,如果有就让这个点对当前线段的两个端点建边,再去掉(l,r)中所有染过色的点,最后把l,r两点染色。处理完所有线段和,如果还存在染色点,就将其对终点建边。线段树的作用就是优化染色和查询的复杂度。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define lson id<<1
#define rson id<<1|1
#define maxn 100009
#define maxm 500009
#define MAX 800009
#define N 200009
#define A 100000
using namespace std;
struct node
{
    int l,r;
    bool f;
};
struct node1
{
    int l;int r;
};
struct node1 p[50009];
struct node tree[MAX];
vector<int>G[200009];
bool inq[maxn];
int d[maxn];
int n,tot;
int first[maxn];
int u[maxm],v[maxm],w[maxm],next1[maxm];
void pushup(int id)
{
    if(tree[lson].f||tree[rson].f)
        tree[id].f=1;
    else
        tree[id].f=0;
}
void build(int l,int r,int id)
{
    tree[id].l=l;tree[id].r=r;tree[id].f=0;
    if(l==r)
        return;
    int m=(l+r)>>1;
    build(l,m,lson);
    build(m+1,r,rson);
}
void update(int l,int r,int id,int val)
{
    if(l<=tree[id].l&&tree[id].r==r)
    {
        tree[id].f=val;
        return;
    }
    int m=(tree[id].l+tree[id].r)>>1;
    if(l<=m)
        update(l,r,lson,val);
    if(r>m)
        update(l,r,rson,val);
    pushup(id);
}
void add(int x,int y,int z)
{
    u[tot]=x,v[tot]=y,w[tot]=z;
    next1[tot]=first[x];
    first[x]=tot++;
}
void query(int l,int r,int id,int v)
{
    if(!tree[id].f)
        return;
    if(tree[id].l==tree[id].r)
    {
        tree[id].f=0;
        for(int i=0;i<G[tree[id].l].size();i++)
        {
            int e=G[tree[id].l][i];
            int tmp=tree[id].l;
            add(e,2*v,abs(tmp-p[v].l));
            add(e,2*v+1,abs(tmp-p[v].r));
        }
        G[tree[id].l].clear();
        return;
    }
    int m=(tree[id].l+tree[id].r)>>1;
    if(l<=m)
        query(l,r,lson,v);
    if(r>m)
        query(l,r,rson,v);
    pushup(id);
}
int spfa()
{
    for(int i=0;i<=2*n+2;i++)
        d[i]=1<<30;
    d[0]=0;
    queue<int>q;
    q.push(0);
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        inq[x]=0;
        for(int e=first[x];e!=-1;e=next1[e])
        {
            if(d[v[e]]>d[x]+w[e])
            {
                d[v[e]]=d[x]+w[e];
                if(!inq[v[e]])
                {
                    inq[v[e]]=1;
                    q.push(v[e]);
                }
            }
        }
    }
    return d[2*n+2];
}
int main()
{
    int s;
    scanf("%d%d",&n,&s);
    memset(first,-1,sizeof(first));
    for(int i=n;i>=1;i--)
        scanf("%d%d",&p[i].l,&p[i].r);
    build(0,N,1);
    s+=A;
    update(s,s,1,1);
	G[s].push_back(0);
    for(int i=1;i<=n;i++)
    {
        p[i].l+=A;p[i].r+=A;
        if(p[i].r-1>=p[i].l+1)
        {
            query(p[i].l+1,p[i].r-1,1,i);
        }
        update(p[i].l,p[i].l,1,1);
        update(p[i].r,p[i].r,1,1);
        G[p[i].l].push_back(2*i);
        G[p[i].r].push_back(2*i+1);
    }
    for(int i=0;i<N;i++)
    {
        for(int j=0;j<G[i].size();j++)
        {
            int e=G[i][j];
            add(e,2*n+2,abs(i-A));
        }
    }
    printf("%d",spfa());
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值