Codeforces Round #504 (rated, Div. 1 + Div. 2, based on VK Cup 2018 Final)

D - Array Restoration

题意:一个n个元素的数组,恰好进行q次修改,第i次修改可以把连续的一段区间赋值为i。求是否可以构造出题目提供的数组,0表示可以填任意值。

题解:
先解决没有0的情况的问题。每次都是区间更新一次,那么查询到最左的i和最右的i(假如存在),那么中间部分的最小值必须>=i,这个看起来就像线段树查询,实际上用st表也可以。当所有的查询都满足并且最大值q存在,那么合法(中间缺的值全都由最大值覆盖掉)。

有0的情况稍微复杂一些,首先假如最大值q不存在,那么随便赋值任意一个0变成最大值就可以了。如果中间的段有0的话,就把这些0强制赋值为i,注意到这样子不会使区间的最小值变大,也就是这样构造是最优的,既使得lr之间连续,也满足无后效性。假如还有0剩余就把0赋值为左右相同的值,但是假如左右也是0怎么办呢?不妨把a[0]设为1,然后每个0的值都从其左边继承。

实现的时候就是一棵最小值线段树和一棵区间更新线段树。最后把标记全部下传给叶子。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN = 200000;
const int INF = 0x3f3f3f3f;
int a[MAXN + 5];

struct SegmentTree1 {
#define ls (o<<1)
#define rs (o<<1|1)
    static const int MAXN = 200000;
    static const int INF = 0x3f3f3f3f;
    int mi[(MAXN << 2) + 5];

    void PushUp(int o) {
        mi[o] = min(mi[ls], mi[rs]);
    }

    void Build(int o, int l, int r) {
        if(l == r) {
            mi[o] = (a[l] == 0 ? INF : a[l]);
        } else {
            int m = l + r >> 1;
            Build(ls, l, m);
            Build(rs, m + 1, r);
            PushUp(o);
        }
    }

    int QueryMin(int o, int l, int r, int ql, int qr) {
        if(ql <= l && r <= qr) {
            return mi[o];
        } else {
            int m = l + r >> 1;
            int res = INF;
            if(ql <= m)
                res = QueryMin(ls, l, m, ql, qr);
            if(qr >= m + 1)
                res = min(res, QueryMin(rs, m + 1, r, ql, qr));
            return res;
        }
    }
#undef ls
#undef rs
} st1;

struct SegmentTree2 {
#define ls (o<<1)
#define rs (o<<1|1)
    static const int MAXN = 200000;
    static const int INF = 0x3f3f3f3f;
    int a[MAXN + 5], lz[(MAXN << 2) + 5];

    void PushDown(int o, int l, int r) {
        if(lz[o]) {
            lz[ls] = lz[o];
            lz[rs] = lz[o];
            lz[o] = 0;
        }
    }

    void Build(int o, int l, int r) {
        if(l == r) {
            a[l] = 0;
        } else {
            int m = l + r >> 1;
            Build(ls, l, m);
            Build(rs, m + 1, r);
        }
        lz[o] = 0;
    }

    void Update(int o, int l, int r, int ql, int qr, int v) {
        if(ql <= l && r <= qr) {
            lz[o] = v;
        } else {
            PushDown(o, l, r);
            int m = l + r >> 1;
            if(ql <= m)
                Update(ls, l, m, ql, qr, v);
            if(qr >= m + 1)
                Update(rs, m + 1, r, ql, qr, v);
        }
    }

    void PushDownAll(int o, int l, int r) {
        if(l == r) {
            a[l] = lz[o];
        } else {
            PushDown(o, l, r);
            int m = l + r >> 1;
            PushDownAll(ls, l, m);
            PushDownAll(rs, m + 1, r);
        }
    }
#undef ls
#undef rs
} st2;

int n, q, maxai, lm[MAXN + 5], rm[MAXN + 5];

void solve0() {
    if(maxai != q) {
        puts("NO");
        return;
    }
    st1.Build(1, 1, n);
    for(int i = 1; i <= q; ++i) {
        if(lm[i] == 0)
            continue;
        if(st1.QueryMin(1, 1, n, lm[i], rm[i]) < i) {
            puts("NO");
            return;
        }
    }
    puts("YES");
    for(int i = 1; i <= n; ++i)
        printf("%d%c", a[i], " \n"[i == n]);
}

void solve1() {
    st1.Build(1, 1, n);
    for(int i = 1; i <= q; ++i) {
        if(lm[i] == 0)
            continue;
        if(st1.QueryMin(1, 1, n, lm[i], rm[i]) < i) {
            puts("NO");
            return;
        }
    }
    if(lm[q] == 0)
        a[lm[0]] = q;
    st2.PushDownAll(1, 1, n);
    a[0] = 1;
    for(int i = 1; i <= n; ++i) {
        if(a[i] == 0)
            a[i] = st2.a[i];
        if(a[i] == 0)
            a[i] = a[i - 1];
    }
    puts("YES");
    for(int i = 1; i <= n; ++i)
        printf("%d%c", a[i], " \n"[i == n]);
}

void test_case() {
    scanf("%d%d", &n, &q);
    maxai = 0;
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        if(a[i] > maxai)
            maxai = a[i];
        if(lm[a[i]] == 0)
            lm[a[i]] = i;
        rm[a[i]] = i;
    }
    if(lm[0] == 0)
        solve0();
    else
        solve1();
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    //scanf("%d", &t);
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

/*
    1. 小数据问题退化:
        输入为0或1会不会有特殊情况?其他的比如最小环要有三个点,强连通分量缩到最后一个点等等。
    2. 内存:
        内存的空间有没有开够?有时有反向边,有时有额外新加的边。线段树开了4倍了吗?
        可持久化数据结构会不会内存溢出?多组数据时vector会不会翻车?字符大小写有考虑吗?
        多组数据有进行初始化吗?memset会不会翻车?
    3. 算术溢出:
        乘法上溢出了?忘记取模了?输入输出用了%d?让无符号数越到负数了?
    4. 习惯:
        函数都有指定返回值吗?返回值非void函数的每一个分支都有显式的返回值吗?确定不会进入的分支可以assert一把。
        Yes和No有没有反,大小写有没有错?有没有搞错n和m?离散化之后的cn有没有错?换行和空格要怎么整?priority要把符号反过来。
    5. 其他bug:
        基环树是树加环,不是链加环。
*/
上一篇:【第001篇】vue+element-ui中集成富文本编辑器(vue-quill-editor)


下一篇:MySQL基于GTID的主从复制