High Elements 题解

solution

学长使用了更强大的做法!!

我们考虑用图来思考这个问题。

image

容易发现,最后的等式条件是 红色点个数+[0-x]=绿色点个数+[0-y]
要满足这个条件,其实一定有一边是 0,调整法容易证明。

对于判定一个后缀是否还存在合法解,我们可以考虑一开始全都是红色点(不妨假设红色点取0),这样我们发现替换掉一个在 LIS 上的点,使得等式左右两边的差变化了2,选一个在红色斜杠矩形中的点会让差变化1,于是我们给每个点赋值 1/2 就可以判断是否有解。

闲话

atcoder 数据太水了,所有目前放了代码的题解都被我 hack 了。

但是其他题解的大致思路都是正确的,这篇题解主要解析如何想到这种思路的。

约定

设当前 \(X\)\(Y\) 中 high 元素的个数分别为 \(C_X,C_Y\),最大值分别为 \(H_X,H_Y\),我们想要判断是否能将剩下的元素分配给 \(X\) 或者 \(Y\) 使得 high 元素个数相等。

假设在分配之后,\(X\) 中新增的 high 元素为 \(a_1,a_2,\cdots,a_{|a|}\)\(Y\) 中新增的 high 元素为 \(b_1,b_2,\cdots,b_{|b|}\),那么他们必须满足:

  • \(H_X<a_1<a_2<\cdots<a_{|a|},H_Y<b_1<b_2<\cdots<b_{|b|}\)
  • \(C_X+|a|=C_Y+|b|\)

思路

首先发现这个题目要同时解决两个序列的构造问题,考虑到我们之前解决过这样的问题,比如传纸条。但是这道题目不适用,我们要是记录两个序列的信息来转移肯定会超时。

于是我们考虑两个序列之间的关系,我猜测肯定有一个可行序列的 high 元素就是原序列的 high 元素,很遗憾我猜错了。于是进行一些调整我们发现,肯定有一个序列的 high 元素包含于原序列的 high 元素之中。考虑调整法证明这个结论,如果两个序列都有 high 元素不是原序列的 high 元素,我们可以交换它们两个的位置,可以发现,交换之后,这两个元素都不会是 high 元素,就意味着 \(|a|-1\) 并且 \(|b|-1\),可以发现等式仍然成立,所以存在这样一组解。

为了方便描述,我们不妨让 \(a\) 都是原序列的 high 元素。

有了这个结论真的非常非常有用,原因是这样我们如果可以找到一个合法的 \(b\),那么就可以唯一对应一个合法的 \(a\),也就是说,确定了合法的 \(b\),就对应了这个解合法(显然这里只确定了 \(a\) 就难以唯一对应了)。接下来就考虑在描述序列 \(b\) 的同时找到合法解。于是我们设 \(b\) 中 high 元素为个数为 \(k\),不是 high 元素的个数为 \(m\),那么显然 \(k+m=|b|\)\(Q-k=|a|\)就有:

\(C_X+|a|=C_Y+|b|\)
\(C_X+Q-k=C_Y+k+m\)
\(C_X-C_Y+Q=2k+m\)

于是我们就需要找到满足这个条件的 \(b\) 就行了,具体操作可以把每个值给权值 \(1\)\(2\) 来 dp 得到。
但是直接 dp 可能复杂度不允许,但是我们发现我们可以通过删除 \(b\) 中的元素来构造当前序列权值 \(x\) 减掉 \(2\) 之后的结果。于是我们只要记录最大的奇权值和偶权值就行,可以用线段树维护。

时间复杂度 \(\mathcal O(n\log n)\)

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 200005, INF = 0x3f3f3f3f;

int n;
int a[N], tag[N];
char ans[N];

struct Segtree {
    #define ls u << 1
    #define rs u << 1 | 1

    int tree[N << 2], v;

    void build(int l, int r, int u) {
        tree[u] = v;
        if (l == r) return;
        int mid = (l + r) >> 1;
        build(l, mid, ls);
        build(mid + 1, r, rs);
    }

    void modify(int p, int x, int l = 1, int r = n, int u = 1) {
        if (l == r) {
            tree[u] = x;
            return; 
        }
        int mid = (l + r) >> 1;
        if (p <= mid) modify(p, x, l, mid, ls);
        else modify(p, x, mid + 1, r, rs);
        tree[u] = max(tree[ls], tree[rs]);
    }

    int query(int L, int R, int l = 1, int r = n, int u = 1) {
        if (L <= l && r <= R) {
            return tree[u];
        }
        int mid = (l + r) >> 1, res = v;
        if (L <= mid) res = max(res, query(L, R, l, mid, ls));
        if (R > mid) res = max(res, query(L, R, mid + 1, r, rs));
        return res;
    }

    #undef ls
    #undef rs
}f[2];

inline bool check(int H, int val) {
    if (val < 0) return false;
    return f[val & 1].query(H, n) >= val;
} 

int main() {
    scanf("%d", &n);
    for (int i = 1, mx = 0; i <= n; i++) {
        scanf("%d", &a[i]);
        if (a[i] > mx) mx = a[i], tag[i] = 1;
    }
    
    f[0].v = 0, f[0].build(1, n, 1);
    f[1].v = -INF, f[1].build(1, n, 1);
    for (int i = n; i >= 1; i--) {
        if (tag[i]) {
            f[0].modify(a[i], f[0].query(a[i] + 1, n) + 2);
            f[1].modify(a[i], f[1].query(a[i] + 1, n) + 2);
        } else {
            f[0].modify(a[i], f[1].query(a[i] + 1, n) + 1);
            f[1].modify(a[i], f[0].query(a[i] + 1, n) + 1);
        }
        if (i < n) tag[i] += tag[i + 1];
    }

    int C = 0, HX = 0, HY = 0;
    for (int i = 1; i <= n; i++) {
        f[0].modify(a[i], 0), f[1].modify(a[i], -INF);
        if (check(HY, tag[i + 1] + C + (a[i] > HX)) || check(max(HX, a[i]), tag[i + 1] - C - (a[i] > HX))) {
            ans[i] = '0';
            C += (a[i] > HX);
            HX = max(HX, a[i]);
        } else {
            ans[i] = '1';
            C -= (a[i] > HY);
            HY = max(HY, a[i]);
        }
    }

    if (C) puts("-1");
    else printf("%s\n", ans + 1);
     
    return 0;
}
posted @ 2026-05-17 15:14  suuugg  阅读(9)  评论(0)    收藏  举报