1. 程式人生 > 實用技巧 >CodeForces - 6E Exposition(尺取法+multiset)

CodeForces - 6E Exposition(尺取法+multiset)

CF6E Exposition

題目大意:

給一個\(n\)個元素的序列,從中挑出最長的子串,要求子串中元素差的最大值不超過\(k\)。問有幾個最長子串,子串長度,以及這幾個子串的起始、終止位置。

思路:

很容易想到尺取法。

我們使用\(multiset\)來完成對維護尺取的區間,因為\(multiset\)具有有序性和可充分性,而且在預設的情況下,該容器內的元素是使\(<\)運算子比較大小,也就是容器內部按升序排列。所以我們可以通過\(crbegin()\)\(cbegin()\)分別取出容器內的最大值和最小值進行模擬。

值得一提的是,在模擬單調佇列的彈出操作時,我們使用

\[MuiltisetName.erase(MuiltisetName.find(value)) \]

來刪除指定值。因為如果直接刪除指定值的話,\(erase\)函式會將所有與指定值相同的元素刪去,就不符合題意了。

Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010;
const int INF = 0x3f3f3f3f;

inline int read(){
    int x=0,f=1;char ch=getchar();
    while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}

int main() {
    int n = read(), k = read();
    vector<pair<int, int> > p;
    multiset<int> s;
    vector<int> h(N);
    int l = 0, max_len = 0;
    for (int r = 0; r < n; r++) {
        h[r] = read();
        s.insert(h[r]);
        while (*s.crbegin() - *s.cbegin() > k)
            s.erase(s.find(h[l++]));
        if (r - l + 1 > max_len) {
            p.clear(); //要記錄最長序列長度的位置
            max_len = r - l + 1;
            p.push_back(make_pair(l + 1, r + 1));
        } else if (r - l + 1 == max_len) {
            p.push_back(make_pair(l + 1, r + 1));
        }
    }
    printf("%d %d\n", max_len, p.size());
    for (auto i : p) {
        printf("%d %d\n", i.first, i.second);
    }
    return 0;
}