[线段树]POJ 2374 Fence Obstacle Course

这道题目先抛开时间效率来分析。

我们可以开一个一维数组w[],数组下标就是区间的端点值,cow刚开始在w[s]处。碰到一个区间后,我们看这个区间会覆盖多少个值(实际就是到前面区间的端点所花的步数),枚举这些值更新当前区间两个端点值,枚举完后,把覆盖的值删掉,添加这两个新的端点值,区间外的值不用考虑。最后在从小到大枚举这些端点值加上距离原点的距离即为答案,当然要从这些值中选择最小的。

这其中涉及到删除区间覆盖的值和添加两端点的值。如果用数组来做,最坏情况每次都是O(n),链表每次遍历也会比较浪费时间。

这里就选择线段树了。线段树删除和添加是logn,每次删除的次数依赖剩余的端点,而每个端点最多被添加一次,删除一次,所以复杂度为2*N*logL,L为区间最大距离。

PS:悲了个剧,用链表来做,500ms过了,而且代码也短

线段树,1500ms过,而且代码量多了几乎1倍

。。。。。。。。

先看链表代码:

#include <cstdio>
#include <cstring>
#include <utility>
#include <iostream>
#include <list>
using namespace std;
#define MP make_pair
#define x first
#define y second
const int INF = 0x3f3f3f3f;
const int maxn = 100010;
typedef pair<int, int> pii;
list<pii> p;
list<pii>::iterator it;
int w[maxn][2];
inline int f(int v)
{
    return v >= 0 ? v : -v;
}
int main()
{
    int n, s;
    int l, r;
    scanf("%d %d", &n, &s);
    for(int i = 0; i < n; i++) scanf("%d %d", &w[i][0], &w[i][1]);
    p.push_back(MP(0, s));
    for(int i = n - 1; i >= 0; i--){
        l = w[i][0], r = w[i][1];
        int ll = INF, rr = INF;
        for(it = p.begin(); it != p.end(); ){
            if(l <= it->y && it->y <= r){
                ll = min(ll, it->x + f(it->y - l));
                rr = min(rr, it->x + f(it->y - r));
                it = p.erase(it);
            }else if(it->y > r)
                break;
            else it++;
        }
        if(ll < INF){
            p.insert(it, MP(ll, l));
            p.insert(it, MP(rr, r));
        }
    }
    int ans = INF;
    for(it = p.begin(); it != p.end(); it++){
        ans = min(ans, it->x + f(it->y));
    }
    printf("%d\n", ans);
}


然后线段树代码:

#include <cstdio>
#include <cstring>
#include <utility>
#include <iostream>
using namespace std;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const int INF = 0x3f3f3f3f;
const int maxn = 200010;
typedef pair<int, int> pii;
int cover[maxn << 2], MinV[maxn << 2], P[maxn << 2];
int w[maxn][2];
void PushUp(int rt)
{
    MinV[rt] = MinV[rt << 1];
    P[rt] = P[rt << 1];
    if(MinV[rt] > MinV[rt << 1 | 1]){
        MinV[rt] = MinV[rt << 1 | 1];
        P[rt] = P[rt << 1 | 1];
    }
}
void update(int p, int c, int l, int r, int rt)
{
    if(l == r) {
        MinV[rt] = c;
        P[rt] = l;
        return ;
    }
    int m = (l + r) >> 1;
    if(p <= m) update(p, c, lson);
    else update(p, c, rson);
    PushUp(rt);
}
pii query(int L, int R, int l, int r, int rt)
{
    if(L <= l && r <= R) return pii(MinV[rt], P[rt]);
    int m = (l + r) >> 1;
    pii u(INF, 0), v;
    if(L <= m) {
        v = query(L, R, lson);
        if(u.first > v.first) u = v;
    }
    if(m < R) {
        v = query(L, R, rson);
        if(u.first > v.first) u = v;
    }
    PushUp(rt);
    return u;
}

inline int f(int x)
{
    return x >= 0 ? x : -x;
}
int main()
{
    int n, s;
    int L = INF, R = -INF;
    int l, r;
    memset(MinV, 0x3f, sizeof(MinV));
    scanf("%d %d", &n, &s);
    for(int i = 0; i < n; i++){
        scanf("%d %d", &w[i][0], &w[i][1]);
        L = min(L, w[i][0]);
        R = max(R, w[i][1]); 
    }
    update(w[n - 1][0], f(s - w[n - 1][0]), L, R, 1);
    update(w[n - 1][1], f(s - w[n - 1][1]), L, R, 1);
    for(int i = n - 2; i >= 0; i--){
        l = w[i][0], r = w[i][1];
        int ll = INF, rr = INF;
        while(true){
            pii u = query(l, r, L, R, 1);
            if(u.first == INF) break;
            ll = min(ll, u.first + f(u.second - l));
            rr = min(rr, u.first + f(u.second - r));
            update(u.second, INF, L, R, 1);
        }
        update(l, ll, L, R, 1);
        update(r, rr, L, R, 1);
    }
    int ans = INF;
    for(int i = L; i <= R; i++){
        pii u = query(i, i, L, R, 1);
        ans = min(ans, u.first + f(u.second));
    }
    printf("%d\n", ans);
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值