「解题报告」[NOI2022] 冒泡排序

前排膜拜 happyguy


感觉这种特殊性质给的很多的题就应该把特殊性质挨个进行分析。

特殊性质 A

首先容易发现: \(V_i \in [0, 1]\),那么 \(a_i \in [0, 1]\)。显然这样不劣。

然后我们相当于有两种限制:一种是区间内都是 \(1\),一种是区间内至少有一个 \(0\)

我们考虑当前有某个序列 \(\{a_i\}\),然后考虑如何使得这个序列更优。发现如果有一个位置放的是 \(1\) 但可以放 \(0\),后面有一个位置放的是 \(0\) 但可以放 \(1\),那么交换这两个数一定不劣。

那么最后的答案一定是存在一个分界线,后面尽可能放 \(1\),前面尽可能放 \(0\)。直接枚举分界线容易做到 \(O(n^2)\),预处理下前后缀答案一类的东西大概可以做到 \(O(n \log n)\)

而仔细分析这个策略,发现我们放 \(0\) 的位置都是尽可能往前放。

特殊性质 B

相当于有若干位置的数已经固定了,其它的数任意选择。

首先有一点,我们填的数肯定都是某个 \(V_i\),不是 \(V_i\) 的调整成等于 \(V_i\) 的显然更优。

考虑贪心,从左往右尽可能填较小的数使得逆序对最小,逆序对相同的选数最小的。发现这样贪心是对的。感性理解下,我不会证明。

然后这样拿颗线段树维护填每个数产生的逆序对数,维护最小值和最小值中最小的下标,每次贪心的做即可。

特殊性质 C

还是根据上面的性质,我们考虑最小值尽可能往前放。区间不相交,那么我们就将最小值都放到区间的左端点上,这转化成了特殊性质 B,但是有若干 \(a_i \ge v\) 的限制。同样可以考虑特殊性质 B 的贪心,每次选大于等于 \(v\) 中的最小值即可。

正解

同样考虑将问题归约到只有 \(a_i = v\)\(a_i \ge v\) 的限制上去。考虑两个 \(v_i < v_j\) 的区间,它们相交的部分肯定不能填 \(v_i\),那么我们就可以把前者的区间缩小到不相交的位置。对于 \(v_i = v_j\) 相等的区间,我们可以用特殊性质 A 的方法,尽可能将 \(v_i\) 往前填,这样也能确定若干 \(a_i = v\)。这样整个问题就归约到性质 C 上去了,直接用性质 C 的做法做即可。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000005;
int T, n, m, V;
int l[MAXN], r[MAXN], v[MAXN];
int fa[MAXN], a[MAXN], mn[MAXN];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
vector<pair<int, int>> segs[MAXN];
vector<int> vals;
struct BinaryIndexTree {
    int a[MAXN];
#define lowbit(x) (x & (-x))
    void add(int d) {
        while (d) {
            a[d]++;
            d -= lowbit(d);
        }
    }
    long long query(int d) {
        long long res = 0;
        while (d <= V) {
            res += a[d];
            d += lowbit(d);
        }
        return res;
    }
    void clear() {
        memset(a, 0, sizeof a);
    }
} bit;
struct SegmentTree {
    struct Node {
        pair<long long, int> mn;
        long long tag;
    } t[MAXN << 2];
#define lc (i << 1)
#define rc (i << 1 | 1)
    void pushDown(int i = 1, int l = 1, int r = V) {
        if (t[i].tag) {
            t[lc].tag += t[i].tag;
            t[rc].tag += t[i].tag;
            t[lc].mn.first += t[i].tag;
            t[rc].mn.first += t[i].tag;
            t[i].tag = 0;
        }
    }
    void pushUp(int i) {
        t[i].mn = min(t[lc].mn, t[rc].mn);
    }
    void build(int i = 1, int l = 1, int r = V) {
        t[i].mn = { 0, l };
        t[i].tag = 0;
        if (l == r) return;
        int mid = (l + r) >> 1;
        build(lc, l, mid);
        build(rc, mid + 1, r);
    }
    void add(int a, int b, int v, int i = 1, int l = 1, int r = V) {
        if (a > b) return;
        if (a <= l && r <= b) {
            t[i].tag += v;
            t[i].mn.first += 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);
        pushUp(i);
    }
    pair<int, long long> query(int a, int b, int i = 1, int l = 1, int r = V) {
        if (a <= l && r <= b) return t[i].mn;
        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 min(query(a, b, lc, l, mid), query(a, b, rc, mid + 1, r));
    }
} st;
int main() {
    // freopen("bubble6.in", "r", stdin);
    // freopen("bubble3.out", "w", stdout);
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n + 1; i++) {
            fa[i] = i;
            a[i] = mn[i] = 0;
        }
        vals.clear();
        for (int i = 1; i <= m; i++) {
            scanf("%d%d%d", &l[i], &r[i], &v[i]);
            vals.push_back(v[i]);
        }
        sort(vals.begin(), vals.end());
        vals.erase(unique(vals.begin(), vals.end()), vals.end());
        V = vals.size();
        for (int i = 1; i <= V; i++) {
            segs[i].clear();
        }
        for (int i = 1; i <= m; i++) {
            v[i] = lower_bound(vals.begin(), vals.end(), v[i]) - vals.begin() + 1;
            segs[v[i]].push_back({l[i], r[i]});
        }
        long long ans = 0;
        for (int i = V; i >= 1; i--) {
            sort(segs[i].begin(), segs[i].end(), 
                [](auto a, auto b) { return a.first > b.first; });
            int lst = n + 1;
            for (auto p : segs[i]) {
                int l = p.first, r = p.second;
                if (r < lst) {
                    int pos = find(l);
                    if (pos > r) {
                        printf("-1\n");
                        goto nxt;
                    }
                    a[pos] = i, lst = pos;
                }
            }
            reverse(segs[i].begin(), segs[i].end());
            for (auto p : segs[i]) {
                int l = p.first, r = p.second;
                for (int j = find(l); j <= r; j = find(j)) {
                    mn[j] = i, fa[j] = j + 1;
                }
            }
        }
        bit.clear();
        st.build();
        for (int i = 1; i <= n; i++) {
            // printf("a[%d]=%d, mn[%d]=%d\n", i, a[i], i, mn[i]);
            if (a[i]) {
                st.add(a[i] + 1, V, 1);
                ans += bit.query(a[i] + 1);
                bit.add(a[i]);
            }
        }
        for (int i = 1; i <= n; i++) {
            if (a[i]) {
                st.add(a[i] + 1, V, -1);
                st.add(1, a[i] - 1, 1);
            } else {
                auto p = st.query(mn[i], V);
                ans += p.first;
                st.add(1, p.second - 1, 1);
            }
        }
        printf("%lld\n", ans);
        nxt:;
    }
    return 0;
}
posted @ 2023-02-15 14:47  APJifengc  阅读(104)  评论(0编辑  收藏  举报