|
思路:
用线段树维护所有线段的分布。 新增加一个fence的时候,将fence的范围[a, b]插入线段树,节点的值为fence的编号,即高度。 那么fence上的某一点就是树的叶子了,从叶子往上一直到根节点的路径中节点的最大值, 就是从fence上的这一点垂直掉下去后,碰到的一个fence了。
这样,我们就可以在O(lgN)时间内知道,从一个fence的端点掉下去会碰到哪个fence了。 不然从后往前一个个找就是O(N)复杂度了。同时N也很大,必然超时。 同时也可以发现,一个fence保存两个值用作动态规划就好了,向左、右走之后,掉到其他fence上面,然后回到基地所用的最短路径。 推的方法很简单,掉到其他fence上面之后,看下是向左走距离短还是向右走距离短。就行了。 这个代码跑到400ms。
#include <stdio.h>

#define MAX_N 50032
#define MAX_R 100032

 struct {
int a, b;
} dp[MAX_N], fences[MAX_N];
int N, S, tree[MAX_R*16];

__inline int max(int a, int b)
  {
return a > b ? a : b;
}

__inline int abs(int a)
  {
return a > 0 ? a : -a;
}

__inline int min(int a, int b)
  {
return a < b ? a : b;
}

void insert(int idx, int start, int end, int left, int right, int val)
  {
int mid;

 if (start == left && right == end) {
tree[idx] = val;
return ;
}
mid = (start + end) / 2;
if (right <= mid)
insert(idx*2, start, mid, left, right, val);
else if (left > mid)
insert(idx*2+1, mid + 1, end, left, right, val);
 else {
insert(idx*2, start, mid, left, mid, val);
insert(idx*2+1, mid + 1, end, mid + 1, right, val);
}
}

int query(int idx, int start, int end, int pos)
  {
int val, mid;

if (start == pos && end == pos)
return tree[idx];
mid = (start + end) / 2;
if (pos <= mid)
val = query(idx*2, start, mid, pos);
else
val = query(idx*2+1, mid + 1, end, pos);
return max(val, tree[idx]);
}

__inline int calc_min(int i, int pos)
  {
if (!i)
return abs(pos - MAX_R);
return min(pos - fences[i].a + dp[i].a, fences[i].b - pos + dp[i].b);
}

int main()
  {
int i;

freopen("e:\\test\\in.txt", "r", stdin);

scanf("%d%d", &N, &S);
S += MAX_R;
 for (i = 1; i <= N; i++) {
scanf("%d%d", &fences[i].a, &fences[i].b);
fences[i].a += MAX_R;
fences[i].b += MAX_R;
dp[i].a = calc_min(query(1, 0, MAX_R*2, fences[i].a), fences[i].a);
dp[i].b = calc_min(query(1, 0, MAX_R*2, fences[i].b), fences[i].b);
insert(1, 0, MAX_R*2, fences[i].a, fences[i].b, i);
}
printf( "%d\n",
min(S - fences[N].a + dp[N].a, fences[N].b - S + dp[N].b)
);

return 0;
}

|