「解题报告」[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;
}