「解题报告」CF1558F Strange Sort

发布时间 2023-05-17 15:51:16作者: APJifengc

我好弱智。

首先发现这东西根本不可做。考虑降值域!把 \(a_i\) 变成 \([a_i \ge k]\) 转化成 01 序列去做,那么最终的答案就是所有 \(k\) 得到的答案的 \(\max\)

先考虑一个 01 序列怎么做。我们考虑求出每一个 \(0\) 到达它该到达的位置所需的时间 \(f_i\)。对于一个 \(0\),我们有两种可能性:

  1. 在向左交换的过程中没有碰到任何一个 \(0\)
  2. 在向左交换的时候碰到了一个 \(0\)

我们发现,当交换过程中碰到一个 \(0\) 后,之后肯定是前一个 \(0\) 怎么走它就怎么走,于是答案就是 \(f_{i - 1} + 1\);如果没有碰到任何一个 \(0\),设这个数的位置为 \(t_i\),那么它的答案就是前面的 \(1\) 的个数 \(k_i\),再加上一个 \((t_i \bmod 1)\)(考虑第一次是否被交换)。即:

\[f_i = \max\{f_{i - 1} + 1, k_i + (t_i \bmod 1)\} \]

设一共有 \(m\)\(1\),那么我们只需要考虑 \(f_m\) 即可。把上面的式子暴力展开,即可得到:

\[f_m = \max_{i=pre+1}^m \{k_i + (t_i \bmod 1) + (m - i)\} \]

其中 \(pre\) 表示满足 \(f_i = 0\)(即一开始就已经在正确的位置)的前缀长度。

把它放到序列上维护,设第 \(i\) 个位置前面有 \(p_i\)\(0\)\(i - p_i\)\(1\),那么其实就是:

\[f_m = \max_{i=pre+1}^n \{[a_i = 0] (p_i + (i \bmod 1) + (m - (i - p_i)))\} \]

\[f_m = \max_{i=pre+1}^n \{[a_i = 0] (2p_i + (i \bmod 1) + m - i)\} \]

这东西很容易在线段树上维护,那么我们每次将一个 \(1\) 改为 \(0\),直接维护即可。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
int n, a[MAXN], p[MAXN];
struct SegmentTree {
    struct Node {
        int val, tag;
    } t[MAXN << 2];
#define lc (i << 1)
#define rc (i << 1 | 1)
    void tag(int i, int v) {
        t[i].tag += v;
        t[i].val += v;
    }
    void pushDown(int i) {
        if (t[i].tag) {
            tag(lc, t[i].tag);
            tag(rc, t[i].tag);
            t[i].tag = 0;
        }
    }
    void add(int a, int b, int v, int i = 1, int l = 1, int r = n) {
        if (a <= l && r <= b) {
            tag(i, v);
            return;
        }
        int mid = (l + r) >> 1;
        pushDown(i);
        if (a <= mid) add(a, b, v, lc, l, mid);
        if (b > mid) add(a, b, v, rc, mid + 1, r);
        t[i].val = max(t[lc].val, t[rc].val);
    }
    int query(int a, int b, int i = 1, int l = 1, int r = n) {
        if (a > b) return -30 * n;
        if (a <= l && r <= b) return t[i].val;
        int mid = (l + r) >> 1;
        pushDown(i);
        if (b <= mid) return query(a, b, lc, l, mid);
        if (a > mid) return query(a, b, rc, mid + 1, r);
        return max(query(a, b, lc, l, mid), query(a, b, rc, mid + 1, r));
    }
    void build(int i = 1, int l = 1, int r = n) {
        t[i].val = t[i].tag = 0;
        if (l == r) return;
        int mid = (l + r) >> 1;
        build(lc, l, mid), build(rc, mid + 1, r);
    }
} st;
int T;
bool b[MAXN];
int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        st.build();
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            p[a[i]] = i;
            b[i] = 1;
        }
        st.add(1, n, -10 * n);
        for (int i = 1; i <= n; i++) {
            st.add(i, i, (i & 1) + i);
        }
        int pre = 0;
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            b[p[i]] = 0;
            while (pre < n && !b[pre + 1]) pre++;
            st.add(p[i], p[i], 10 * n);
            st.add(p[i], n, -2);
            ans = max(ans, i + st.query(pre + 1, n));
        }
        printf("%d\n", ans);
    }
    return 0;
}