1. 程式人生 > 其它 >Educational Codeforces Round 112 E、Boring Segments

Educational Codeforces Round 112 E、Boring Segments

原題網址

https://codeforces.com/contest/1555/problem/E

題目大意

有n個區間。每個區間是[1,m]的子區間。從a可以一步走到b的充要條件是存在區間同時覆蓋[a,b]。若n個區間中取出一些區間後,可以只通過被取出的區間從1走到m,則稱這些被取出的區間組成的集合A是好的。每個區間有價值w,求一個好的集合A的所有元素中,最大價值與最小价值的差的最小值。

資料結構

線段樹,支援以下兩種操作:

  • 插入或刪除區間
  • 查詢當前區間集合是否為好的

若一個區間集是好的,則1.5,2.5,...,m-0.5都至少被一個區間覆蓋,區間[a,b]可以覆蓋a+0.5,...,b-0.5。所以我們可以將每個區間的右端點減一後再操作,線段樹第a個元素表示a+0.5被多少個區間覆蓋。這樣只要查詢線段樹中[1,m-1]最小值是否為0,不為0即好的。

由於m較大,必須使用懶修改方法,每個節點增加lazy值(區別於真正的val值),注意點:

  • 訪問某節點時,先進行push操作(將本節點lazy值轉給val值,如果還有孩子,則lazy值傳遞給孩子,清空本節點lazy值),不管被查區間是多少(若l>r也要push,因為只要能遞迴到,就表示該點實際上表示了一個區間,需要把lazy值轉給val值)。
  • 修改時,如果某個節點表示的區間全都要修改,只修改該節點lazy值,不遞迴。改完後必須進行push操作,把lazy值轉成val並傳遞給孩子。
  • 遞迴返回時,將兩個孩子的val組合成本節點的val。

解題思路

本題為最優化問題。顯然儘可能選價值差較小的區間。
先將所有區間按價值排序。
用雙指標維護一個已知價值最小值,找到價值最大值最小的好的區間範圍。
列舉所有最小值,根據最小值指標和線段樹查詢結果移動最大值指標並更新最後答案(用最大值-最小值更新ans)。

我的程式碼

#include <bits/stdc++.h>

using namespace std;

struct seg {
    int l, r, w;
};

bool operator<(const seg& a, const seg& b) { return a.w < b.w; }

vector<seg> seglist;
vector<int> seglazy;
vector<int> segtree;

void push(int v) {
    if (2 * v + 1 < segtree.size()) {
        seglazy[2 * v] += seglazy[v];
        seglazy[2 * v + 1] += seglazy[v];
    }
    segtree[v] += seglazy[v];
    seglazy[v] = 0;
}

void addvalue(int v, int tl, int tr, int l, int r, int val) {
    push(v);
    if (l > r) return;
    if (l == tl && r == tr) {
        seglazy[v] += val;
        push(v);
        return;
    }
    int tm = (tl + tr) / 2;
    addvalue(v * 2, tl, tm, l, min(r, tm), val);
    addvalue(v * 2 + 1, tm + 1, tr, max(l, tm + 1), r, val);
    segtree[v] = min(segtree[2 * v], segtree[2 * v + 1]);
}

int get() { return segtree[1] + seglazy[1]; }

int main() {
    ios::sync_with_stdio(false);

    int n, m;
    cin >> n >> m;
    seglist.resize(n);
    for (int i = 0; i < n; ++i) {
        cin >> seglist[i].l >> seglist[i].r >> seglist[i].w;
        --seglist[i].l;
        --seglist[i].r;
    }
    sort(seglist.begin(), seglist.end());
    segtree.resize(m << 2, 0);
    seglazy.resize(m << 2, 0);

    int lp = 0, rp = 0;
    int ans = 1e9;

    for (lp = 0; lp < n; ++lp) {
        while (rp < n && get() == 0) {
            addvalue(1, 0, m - 2, seglist[rp].l, seglist[rp].r - 1, 1);
            ++rp;
        }
        if (get() == 0) break;
        ans = min(ans, seglist[rp - 1].w - seglist[lp].w);
        addvalue(1, 0, m - 2, seglist[lp].l, seglist[lp].r - 1, -1);
    }
    cout << ans << endl;
    return 0;
}