POJ2374 Fence Obstacle Course 【线段树】

题目链接

POJ2374

题解

题意:

给出\(n\)个平行于\(x\)轴的栅栏,求从一侧栅栏的某个位置出发,绕过所有栅栏到达另一侧\(x = 0\)位置的最短水平距离

往上说都是线段树优化dp

我写了一个奇怪的线段树过了,似乎并没有和dp沾边

因为每次都是从某个栅栏的端点出发,到达某个位置的值等于[所有这些可出发的端点已产生的代价 + 到达这个点的距离] 的最小值

就用线段树维护每个区间

\(lm[u]\)表示在这个区间内的端点到达左端点的最小代价

\(rm[u]\)表示到右端点的最小代价

然后每出现一层栅栏,计算出左右端点来更新线段树,同时清空被新栅栏覆盖的区间【因为这些端点已经无法直接到达下一层区间了】

PS:

一开始看错题了,栅栏的顺序是反过来的

在察觉看错题之前,拿了往上若干AC程序对拍

拍出一个惊人小数据,竟所有程序输出都不一样!!

5 0

3 4

-4 5

-4 0

1 3

2 3

最终画图钦定,,我把它们hack掉了

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define cls(s) memset(s,0,sizeof(s))
#define ls (u << 1)
#define rs (u << 1 | 1)
using namespace std;
const int maxn = 200005,maxm = 100005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
const int ll = 1,rr = 200001,P = 100001;
int lm[maxn << 2],rm[maxn << 2],tag[maxn << 2];
void upd(int u,int l,int r){
    int mid = l + r >> 1;
    lm[u] = min(lm[ls],lm[rs] + (mid - l + 1));
    rm[u] = min(rm[rs],rm[ls] + (r - mid));
}
void pd(int u){
    if (tag[u]){
        lm[ls] = rm[ls] = lm[rs] = rm[rs] = INF;
        tag[ls] = tag[rs] = 1;
        tag[u] = 0;
    }
}
void modify(int u,int l,int r,int pos,int v){
    if (l == r){
        lm[u] = min(lm[u],v);
        rm[u] = min(rm[u],v);
        return;
    }
    pd(u);
    int mid = l + r >> 1;
    if (mid >= pos) modify(ls,l,mid,pos,v);
    else modify(rs,mid + 1,r,pos,v);
    upd(u,l,r);
}
void reset(int u,int l,int r,int L,int R){
    if (l >= L && r <= R){lm[u] = rm[u] = INF; tag[u] = 1; return;}
    pd(u);
    int mid = l + r >> 1;
    if (mid >= L) reset(ls,l,mid,L,R);
    if (mid < R) reset(rs,mid + 1,r,L,R);
    upd(u,l,r);
}
int query(int u,int l,int r,int pos){
    if (l == r) return min(lm[u],rm[u]);
    pd(u);
    int mid = l + r >> 1;
    if (mid >= pos) return min(lm[rs] + (mid - pos + 1),query(ls,l,mid,pos));
    return min(rm[ls] + (pos - mid),query(rs,mid + 1,r,pos));
}
void build(int u,int l,int r){
    if (l == r){lm[u] = rm[u] = INF; return;}
    int mid = l + r >> 1;
    build(ls,l,mid);
    build(rs,mid + 1,r);
    upd(u,l,r);
}
int ql[maxn],qr[maxn];
int main(){
    int n,s,l,r,ld,rd;
    while (~scanf("%d%d",&n,&s)){
        s += P;
        build(1,ll,rr);
        modify(1,ll,rr,s,0);
        for (int i = 1; i <= n; i++) ql[i] = read(),qr[i] = read();
        for (int i = n; i; i--){
            l = ql[i] + P; r = qr[i] + P;
            ld = query(1,ll,rr,l);
            rd = query(1,ll,rr,r);
            if (l + 1 <= r - 1) reset(1,ll,rr,l + 1,r - 1);
            modify(1,ll,rr,l,ld);
            modify(1,ll,rr,r,rd);
        }
        printf("%d\n",query(1,ll,rr,P));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/9021839.html

时间: 2024-10-16 13:51:54

POJ2374 Fence Obstacle Course 【线段树】的相关文章

POJ2374 Fence Obstacle Course

题意 Language:Default Fence Obstacle Course Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 2900 Accepted: 1042 Description Farmer John has constructed an obstacle course for the cows' enjoyment. The course consists of a sequence of N fences

Codeforces 484E. Sign on Fence 可持久化线段树

大概题意: 给一数组a,问在某一区间L~R中,问对于连续的长为W的一段中最小的数字的最大值是多少. 显然可以转化成二分高度然后判断可行性的问题. 考虑到高度肯定为数组中的某一个值,将数组从大到小排序. 建n棵线段树,对于第 i 棵线段树,将 大于等于a[i] 的叶子的值设置为1,其他的叶子设置为0,问题就转化成了用线段树求某一区间中最长的连续的1的个数,这是一个线段树的经典问题,可以通过维护每个节点的 左边连续和,右边连续和,连续和的最大值 得到. 由于空间问题,不可能建立10^5棵线段树,考虑

Codeforces 484E Sign on Fence(可持久化线段树+二分)

题目链接:Codeforces 484E Sign on Fence 题目大意:给定给一个序列,每个位置有一个值,表示高度,现在有若干查询,每次查询l,r,w,表示在区间l,r中, 连续最长长度大于w的最大高度为多少. 解题思路:可持久化线段树维护区间合并,前端时间碰到一题可持久化字典树,就去查了一下相关论文,大概知道了是 什么东西. 将高度按照从大到小的顺序排序,然后每次插入一个位置,线段树维护最长连续区间,因为插入是按照从大到小的顺 序,所以每次的线段树中的连续最大长度都是满足高度大于等于当

(困难) CF 484E Sign on Fence,整体二分+线段树

Bizon the Champion has recently finished painting his wood fence. The fence consists of a sequence of n panels of 1 meter width and of arbitrary height. The i-th panel's height is hi meters. The adjacent planks follow without a gap between them. Afte

可持续化线段树(例题Sign on Fence[Codeforces 484E])

刚刚学习的想记录一下: 第一次接触可持续化线段树,很懵... 题目: 题目描述 izon the Champion has recently finished painting his wood fence. The fence consists of a sequence of n panels of 1 meter width and of arbitrary height. The i-th panel's height is hi meters. The adjacent planks

「CF484E」Sign on Fence「整体二分」「线段树」

题意 给定一个长度为\(n\)的正整数序列,第\(i\)个数为\(h_i\),\(m\)个询问,每次询问\((l, r, w)\),为\([l, r]\)所有长度为\(w\)的子区间最小值的最大值.(类似于一类特殊的直方图最大子矩形问题) \(1 \leq n, m \leq 10^5\) 题解 我们考虑二分答案,这样\(n\)个数变成\(01\),若\(h_i\geq mid\)则为\(0\),否则为\(1\) 每次就相当于查询存不存在长度为\(w\)的连续\(1\).用线段树维护. 这有个问

Fence Obstacle Course

Fence Obstacle Course 有n个区间自下而上有顺序的排列,标号\(1\sim n\),第i个区间记做\([l_i,r_i]\),现在从第n个区间的起点s出发(显然s在\([l_n,r_n]\)内),每次可以选择移动到所在区间的左端点或者右端点,然后跳下去,到达第一个碰到的区间,继续进行相同操作,定义第0个区间为无限延伸,求到第0个区间的位置0的最小水平移动距离,\(n\leq 50000,-1000000\leq l_i,r_i\leq 100000\). 解 思路一 注意到每

【bzoj3387-跨栏训练】线段树+dp

我们可以想到一个dp方程:f[i][0]表示当前在i个栅栏的左端点,f[i][1]表示在右端点. 分两种情况: 第一种:假设现在要更新线段gh的左端点g,而它下来的路径被ef挡住了,那么必定是有ef来更新g. 为什么呢?因为其它点走到g必定要下落,比如说d到g,就相当于d到f再到g. 第二种:假设到ab的路径上没有东西挡着,那就可以直接从源点走过去再直接下落. 按照从上到下的顺序插入线段,线段树就是找当前的某个点被哪条id最大(也就是最低的)线段所覆盖. 1 #include<cstdio>

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include