Siyuan

「2019 Multi-University Training Contest 2」Longest Subarray
「2019 Multi-University Training Contest 2」Longest Subarray
扫描右侧二维码阅读全文
29
2019/07

「2019 Multi-University Training Contest 2」Longest Subarray

题目链接:HDU 6602

你有一个长度为 $n$ 的序列 $a$ 和两个整数 $C, K$ 满足序列中的所有元素 $1 \le a_{i} \le C$。

我们定义一个连续子序列 $a_{l}, a_{l + 1}, \dots, a_{r}$ 是「好的」当且仅当:

$$ \forall x \in [1, C], \sum_{i = l} ^ {r} [a_{i} = x] = 0\ \text{或}\ \sum_{i = l} ^ {r} [a_{i} = x] \ge K $$

抽象地,如果一个数字在子序列中出现过,那么它的出现次数必须不少于 $K$ 次。

他需要求出「好的」连续子序列的最长长度。

本题有多组数据。

数据范围:$1 \le n, C, K \le 10 ^ 5$,$1 \le a_{i} \le C$,$1 \le \sum n, \sum C, \sum K \le 5 \times 10 ^ 5$。


Solution

发现对于每个位置 $i$,以 $i$ 为右端点时,左端点一定在一个区间内。具体地,左端点的可行区间为 $[1, \text{从}\ i\ \text{往前第}\ K\ \text{个值为}\ a_i\ \text{的位置}]$。

有一种比较简单的做法为,在不合法的区间打标记(区间 $+1$),线段树维护区间 最小值最靠左的最小值位置。如果最小值为 $0$ 则最靠左的位置即为左端点的最优解。

注意每次区间修改时需要消除上一次的影响。

时间复杂度:$\mathcal O(\sum n \log n)$。


Code

#include <cstdio>
#include <algorithm>
#include <vector>

const int N = 1e5 + 5;
const int INF = 0x7f7f7f7f;

int n, c, k, a[N];
std::vector<int> vec[N];

template <int MAX>
struct SegmentTree {
    #define lson p << 1
    #define rson p << 1 | 1

    static const int N = MAX << 2;

    int tag[N];
    std::pair<int, int> mn[N];

    void pushUp(int p) {
        mn[p] = std::min(mn[lson], mn[rson]);
    }
    void update(int p, int v) {
        mn[p].first += v, tag[p] += v;
    }
    void pushDown(int p) {
        if (tag[p]) {
            update(lson, tag[p]);
            update(rson, tag[p]);
            tag[p] = 0;
        }
    }
    void build(int p, int l, int r) {
        mn[p].first = tag[p] = 0, mn[p].second = l;
        if (l == r) return;
        int mid = (l + r) >> 1;
        build(lson, l, mid);
        build(rson, mid + 1, r);
        pushUp(p);
    }
    void modify(int p, int l, int r, int x, int y, int v) {
        if (x == l && r == y) {
            update(p, v);
            return;
        }
        pushDown(p);
        int mid = (l + r) >> 1;
        if (y <= mid) {
            modify(lson, l, mid, x, y, v);
        } else if (x > mid) {
            modify(rson, mid + 1, r, x, y, v);
        } else {
            modify(lson, l, mid, x, mid, v);
            modify(rson, mid + 1, r, mid + 1, y, v);
        }
        pushUp(p);
    }
    std::pair<int, int> query(int p, int l, int r, int x, int y) {
        if (x == l && r == y) return mn[p];
        pushDown(p);
        int mid = (l + r) >> 1;
        if (y <= mid) {
            return query(lson, l, mid, x, y);
        } else if (x > mid) {
            return query(rson, mid + 1, r, x, y);
        } else {
            return std::min(query(lson, l, mid, x, mid), query(rson, mid + 1, r, mid + 1, y));
        }
    }

    #undef lson
    #undef rson
};

SegmentTree<N> seg;

int main() {
    for (; scanf("%d%d%d", &n, &c, &k) == 3; ) {
        if (k <= 1) {
            for (int i = 1; i <= n; i++) scanf("%*d");
            printf("%d\n", n);
            continue;
        }
        seg.build(1, 1, n);
        for (int i = 1; i <= c; i++) vec[i].clear();
        int ans = 0;
        for (int i = 1, x; i <= n; i++) {
            scanf("%d", &x);
            int s = vec[x].size();
            if (s > 0) {
                seg.modify(1, 1, n, s >= k ? vec[x][s - k] + 1 : 1, vec[x][s - 1], -1);
            }
            seg.modify(1, 1, n, s >= k - 1 ? vec[x][s - k + 1] + 1 : 1, i, 1);
            auto now = seg.query(1, 1, n, 1, i);
            if (now.first == 0) ans = std::max(ans, i - now.second + 1);
            vec[x].emplace_back(i);
        }
        printf("%d\n", ans);
    }
    return 0;
}

发表评论