首页 > 其他分享 >「解题报告」[NOI2022] 冒泡排序

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

时间:2023-02-15 14:56:42浏览次数:40  
标签:long int mid 冒泡排序 NOI2022 解题 MAXN segs 性质

前排膜拜 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;
}

标签:long,int,mid,冒泡排序,NOI2022,解题,MAXN,segs,性质
From: https://www.cnblogs.com/apjifengc/p/17122387.html

相关文章

  • 一维数组的冒泡排序
    1#include<stdio.h>2intmain(intargc,constchar*argv[])3{4inti,j,t,count;5inta[]={1,85,45,12,14,12,14,78,45,69};6intn=siz......
  • 剑指 Offer 32 - III. 从上到下打印二叉树 III(java解题)
    目录1.题目2.解题思路3.数据类型功能函数总结4.java代码从上到下打印二叉树系列题目1.剑指Offer32-I.从上到下打印二叉树(java解题)2.剑指Offer32-II.从上......
  • 剑指 Offer 32 - II. 从上到下打印二叉树 II(java解题)
    目录1.题目2.解题思路3.数据类型功能函数总结4.java代码5.踩坑记录1.题目从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。例如:给定......
  • 「解题报告」[省选联考 2021 A/B 卷] 图函数
    我不会最短路了?显然每对点能对答案造成的贡献是一个前缀,考虑求出每对点能造成贡献的最大时间。首先能发现,如果\(v>v'\),那么假如\(v\tov'\tou\),那么\(v'\tou\)......
  • 成都七中 解题报告
    点分树有这么一个性质:你总能找到一个点,使得原树中这个点所在的连通块在这个点的子树内(如果整棵树没有被破坏,这个点就是根节点)。这个结论过于显然,证明只有一句话:点分树上的......
  • CF1781D 解题乱弹
    abc1057510554老师说,搞这种数论题,就可以在CF上numbertheory板刷一个1300-1900就可以了。然后发现连1800的题都做不出来,我可以退役力QAQ观察到\(n\)很小,也......
  • 剑指 Offer 32 - I. 从上到下打印二叉树(java解题)
    目录1.题目2.解题思路3.数据类型功能函数总结4.java代码1.题目从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。 例如:给定二叉树: [3,9,......
  • 「解题报告」[省选联考 2021 A 卷] 矩阵游戏
    啥都不会了!啥都不会了!啥都不会了!啥都不会了!啥都不会了!啥都不会了!啥都不会了!啥都不会了!啥都不会了!啥都不会了!啥都不会了!啥都不会了!啥都不会了!啥都不会了!啥都不会了!啥都不会......
  • [POI2011]MET-Meteors 解题报告
    语言系统紊乱了QAQ这道题感觉不是很难鸭qwq。先只考虑一个国家,怎么做?很显然,就直接二分一下就行了。判定答案可以维护一个差分数组,然后最后对它做一个前缀和,再求一下这......
  • 冒泡排序
    正文:此题吾用的是冒泡排序,只有两个方面:排序再去重排序:每个数比较后一个数,如果大就交换位置;去重:有一个变量f,f依次等于每一个数组值(初始为第一个数,从第二个开始......