团体程序设计天梯赛 森森快递(线段树 + 贪心)

/**
因为一个区间[l, r]的最大货运量就是min([l, r]), 对于两个区间[l, r], [L, R] 假设(r <= R)
1. 两个区间完全不相交, 则最大货运量就是min([l, r]) + min([L, R]),谁先取谁后取都是一样

2.[l, r]完全包含于[L, R],那么肯定选择[l, r]且取其最大货运量,因为min[l, r] >= min[L, R],如果选择两个订单的话,最大货运量都
是min[l, r], 但是单独取[l, r]这个区间, 那么对周围影响的区间都小, 往左往右才可能有更优的取值, 如果[L, R]这个区间要取的话,
这里的每个值都要减去一个值,且最终[l, r]的结果是一样的, 所以肯定取[l,r]且保证货运量最大

3.两区间相交,如果若干个区间已经按右端点从小到大排好序,相同按左端点排序,假设相交于[L, r], 那么两个区间的最大值就是
min{min[L, r], min[l, r] + min[L, R]}这个值是不变的,但是如果[L, R]取得大的话, 对后面的可能相交区间最小值影响就大, 所以要对
l, r取尽可能大, L, R取尽可能小

所以就是: 区间已经按右端点从小到大排好序,相同按左端点排序, 然后从左往右,遇到一个区间就取其区间最小值加到ans上去, 然后更新这个区间
*/

#include<bits/stdc++.h>
typedef long long ll;
const ll maxn = 2e5 + 10;
const ll INF = 1e10;
using namespace std;


typedef pair<ll, ll> pa;
ll n, m, T, kase = 1;
ll C[maxn * 4], lazy[maxn * 4], d[maxn];
pa lst[maxn];

void push_down(ll o) {
    ll o1 = o << 1, o2 = o << 1 | 1;
    if(!lazy[o]) return ;
    lazy[o1] += lazy[o]; lazy[o2] += lazy[o];
    C[o1] -= lazy[o]; C[o2] -= lazy[o]; lazy[o] = 0;
}

void build(ll o, ll l, ll r) {
    lazy[o] = 0;
    if(l == r) { C[o] = d[l]; return ; }
    ll mid = (l + r) >> 1;
    build(o << 1, l, mid);
    build(o << 1 | 1, mid + 1, r);
    C[o] = min(C[o << 1], C[o << 1 | 1]);
}

ll ql, qr, data;
ll query_min(ll o, ll l, ll r) {
    if(l >= ql && r <= qr) return C[o];
    if(l > qr || r < ql) return INF;
    ll mid = (l + r) >> 1;
    push_down(o);
    ll p1 = query_min(o << 1, l, mid);
    ll p2 = query_min(o << 1 | 1, mid + 1, r);
    C[o] = min(C[o << 1], C[o << 1 | 1]);
    return min(p1, p2);
}

void update(ll o, ll l, ll r) {
    if(l >= ql && r <= qr) {
        C[o] -= data; lazy[o] += data;
        return ;
    }
    if(l > qr || r < ql) return ;
    push_down(o);
    ll mid = (l + r) >> 1;
    update(o << 1, l, mid);
    update(o << 1 | 1, mid + 1, r);
    C[o] = min(C[o << 1], C[o << 1 | 1]);
}

int main() {
    while(scanf("%lld %lld", &n, &m) != EOF) {
        for(ll i = 0; i < n - 1; i++) scanf("%lld", &d[i]);
        build(1, 0, n - 2);
        for(ll i = 0; i < m; i++) {
            scanf("%lld %lld", &ql, &qr);
            if(ql > qr) swap(ql, qr); qr--;
            lst[i] = pa(qr, ql);
        }
        ll ans = 0; sort(lst, lst + m);
        for(int i = 0; i < m; i++) {
            ql = lst[i].second; qr = lst[i].first;
            ll mt = query_min(1, 0, n - 2);
            ans += mt; data = mt; update(1, 0, n - 2);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值