【2020ICPC·小米 网络选拔赛第一场-E】Phone Network

题目链接:https://ac.nowcoder.com/acm/contest/7501/E

题目大意

给定\(n\)个数的序列,对于每一个\(m\),求最短的涵盖从\(1\)\(i\)的线段的长度。

思路

看了题解之后只剩下:妙啊。

题解已经说得很清楚了,这里复读一遍重新整理一下自己的思路。

\(R_{i,l}\)代表以\(l\)为线段左端点,包含数从\(1\)~\(i\)中所有数字的最近右端点。

对于\(i+1\)来说,其所在的位置为\(p_{1},p_{2},p_{3},...,p_{k}\)。这些数将长度为\(n\)的区间划分为若干部分,如下所示:

[1, \(p_{1}\)],[\(p_{1}+1\), \(p_{2}\)],...,[\(p_{k}+1\),\(n\)]

对于每一个区间,其内部所有的 \(l\) 在从 \(R_{i,l}\) 转移至 \(R_{i+1,l}\) 时,式子为\(R_{i+1,l}\) = \(max(R_{i,l}, \ p_{k})\)
其中,\(p_{k}\)代表其所在区间的右端点。

特别的,当所在的 \(l\) 位于最后一个线段中,不可能构成包含数从 \(1\)~\(i\) 中所有数字的线段,此时应将 \(R_{i+1,l}\) 置为无穷大,也就是取不到

显然在任何情况下 \(R_{i,l}\)单调递增的。最后,将问题转移成了对于每一个区间,找到 \(R_{i,l} < p_{k}\) 的一段数并将其值赋为 \(p_{k}\) 。这个操作可以利用线段树进行维护。

线段树维护过程:

用结构体存储线段树每个节点的左端点和右端点,当被赋值时,因为时区间同时赋值,因此所在区间的最短线段的长度记为 \(val - T[rt].r + 1\)

在查询 \(R_{i,l} < p_{k}\) 的左端点时尽可能地往左边走,在查询右端点时尽可能地往右边走,如果整个区间内没有符合的情况则跳过赋值。

AC代码

#include <bits/stdc++.h>

#define SZ(x) (int)x.size()
#define pii pair<int, int>
#define mp make_pair
#define inf 0x3f3f3f3f
#define pb push_back
using namespace std;
const int MAXN = 2e5 + 5;

int init_val[MAXN];

class SEG {
public:
    struct node {
        int l, r;
        int val, minn;  // val是维护的右端点,minn是线段长度
    } T[MAXN << 2];
    int lazy[MAXN << 2];

    inline void push_up(int rt) {
        T[rt].minn = min(T[rt << 1].minn, T[rt << 1 | 1].minn);
        T[rt].val = min(T[rt << 1].val, T[rt << 1 | 1].val);
    }

    void build(int rt, int l, int r) {
        T[rt].l = l, T[rt].r = r;
        if (l == r) {
            T[rt].minn = init_val[l] - r + 1;
            T[rt].val = init_val[l];
            return;
        }
        int mid = (l + r) >> 1;
        build(rt << 1, l, mid), build(rt << 1 | 1, mid + 1, r);
        push_up(rt);
    }

    inline void push_down(int rt) {
        if (lazy[rt]) {
            T[rt << 1].val = lazy[rt], T[rt << 1 | 1].val = lazy[rt];
            T[rt << 1].minn = lazy[rt] - T[rt << 1].r + 1, T[rt << 1 | 1].minn = lazy[rt] - T[rt << 1 | 1].r + 1;
            lazy[rt << 1] = lazy[rt], lazy[rt << 1 | 1] = lazy[rt];
            lazy[rt] = 0;
        }
    }

    int query_left(int rt, int L, int R, int v) {
        if (T[rt].r < L || T[rt].l > R) return -1;
        if (T[rt].l == T[rt].r) return T[rt].l;
        push_down(rt);
        int ans = -1;
        if (T[rt << 1].val < v) ans = query_left(rt << 1, L, R, v);
        if (ans == -1 && T[rt << 1 | 1].val < v) ans = query_left(rt << 1 | 1, L, R, v);
        return ans;
    }

    int query_right(int rt, int L, int R, int v) {
        if (T[rt].r < L || T[rt].l > R) return -1;
        if (T[rt].l == T[rt].r) return T[rt].l;
        push_down(rt);
        int ans = -1;
        if (T[rt << 1 | 1].val < v) ans = query_right(rt << 1 | 1, L, R, v);
        if (ans == -1 && T[rt << 1].val < v) ans = query_right(rt << 1, L, R, v);
        return ans;
    }

    void change(int rt, int L, int R, int v) {
        if (L <= T[rt].l && T[rt].r <= R) {
            T[rt].val = v;
            T[rt].minn = v - T[rt].r + 1;
            lazy[rt] = v;
            return;
        }
        push_down(rt);
        int mid = (T[rt].l + T[rt].r) >> 1;
        if (L <= mid) change(rt << 1, L, R, v);
        if (R > mid) change(rt << 1 | 1, L, R, v);
        push_up(rt);
    }

} tree;

int a[MAXN];
vector<int> vec[MAXN];

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) vec[a[i]].pb(i);

    int pos = inf;
    for (int i = n; i >= 1; i--) {
        if (a[i] == 1) pos = i;
        init_val[i] = pos;
    }
    tree.build(1, 1, n);
    printf("%d", tree.T[1].minn);   // m = 1

    for (int i = 2; i <= m; i++) {
        for (int j = 0; j < SZ(vec[i]); j++) {
            int l, r, p = vec[i][j];
            if (j == 0) { // segment no 1
                l = 1, r = vec[i][j];
            } else l = vec[i][j - 1] + 1, r = vec[i][j];
            int left = tree.query_left(1, l, r, p);
            int right = tree.query_right(1, l, r, p);
            if (left == -1 && right == -1) {

            } else {
                tree.change(1, left, right, p);
            }
        }
        if (vec[i][SZ(vec[i]) - 1] == n) {}
        else {
            tree.change(1, vec[i][SZ(vec[i]) - 1] + 1, n, inf);   // 忘记+1导致debug了好久
        }
        printf(" %d", tree.T[1].minn);
    }
}

附上一组样例:

10 4
1 1 2 3 1 2 1 4 1 4
posted @ 2020-10-26 15:28  tudouuuuu  阅读(193)  评论(2编辑  收藏  举报