题意
传送门 Codeforces 1828D2 Range Sorting (Hard Version)
题解
先考虑简单情况,即 n ≤ 5 ⋅ 1 0 3 n\leq 5\cdot 10^3 n≤5⋅103,可以直接枚举 O ( n 2 ) O(n^2) O(n2) 个子区间。固定左端点,拓展右端点的同时维护需要排序的段(可以通过求解新增元素在有序区间的插入位置做到)。总时间复杂度 O ( n 2 log n ) O(n^2\log n) O(n2logn)。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct BIT {
vector<int> a;
BIT(int n) : a(n + 1) {}
void add(int i, int d) {
while (i < (int)a.size()) {
a[i] += d;
i += i & -i;
}
}
int get(int i) {
int s = 0;
while (i > 0) {
s += a[i];
i -= i & -i;
}
return s;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int tt;
cin >> tt;
while (tt--) {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
auto xs = a;
sort(xs.begin(), xs.end());
xs.erase(unique(xs.begin(), xs.end()), xs.end());
for (int &x : a) {
x = lower_bound(xs.begin(), xs.end(), x) - xs.begin();
}
ll res = 0;
for (int d = 0; d < n; ++d) {
vector<pair<int, int>> ops;
int cost = 0;
BIT bit(xs.size());
for (int i = 0; i + d < n; ++i) {
int x = a[i + d];
int pos = bit.get(x);
if (pos != i) {
int r = i + 1;
int l = pos;
while (!ops.empty() && ops.back().second > l) {
l = min(l, ops.back().first);
cost -= ops.back().second - ops.back().first - 1;
ops.pop_back();
}
cost += r - l - 1;
ops.push_back({l, r});
}
res += cost;
bit.add(x + 1, 1);
}
}
cout << res << '\n';
}
return 0;
}
考虑 n ≤ 3 ⋅ 1 0 5 n\leq 3\cdot 10^5 n≤3⋅105 的情况,此时难以直接枚举区间。由于排序开销是区间长度减一,需要尽可能地将区间分成尽可能多的段进行排序。对于 [ l , r ) [l,r) [l,r) 可以分段的情况,存在一个位置 k k k,满足 max l ≤ i < k a i < min k ≤ i < r a i \max_{l\leq i<k}a_{i} < \min_{k\leq i<r}a_{i} maxl≤i<kai<mink≤i<rai。枚举满足 min k ≤ i < r a i = a j \min_{k\leq i<r}a_{i} = a_{j} mink≤i<rai=aj 的位置 j j j,找到其左右最邻近的满足 a x < a j , a y < a j a_x<a_j,a_y<a_j ax<aj,ay<aj 的位置 x , y x,y x,y;观察到此时分界点 k k k 仅可能为 x + 1 x + 1 x+1。令 z z z 为最大的满足 z < x , a z > a j z<x,a_z>a_j z<x,az>aj 的位置。则可分段的区间 [ l , r ) [l,r) [l,r) 满足 z < l ≤ x , i < r ≤ y z<l\leq x,i<r\leq y z<l≤x,i<r≤y。std::set 维护值域分别大于 / 小于 a j a_j aj 的位置集合,总时间复杂度 O ( n log n ) O(n\log n) O(nlogn)。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int tt;
cin >> tt;
while (tt--) {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
ll res = 0;
for (int d = 1; d <= n; ++d) {
res += (ll)(d - 1) * (n - d + 1);
}
vector<int> ord(n);
iota(ord.begin(), ord.end(), 0);
sort(ord.begin(), ord.end(), [&](int i, int j) {
return a[i] < a[j];
});
set<int> big{-1, n}, small{-1, n};
for (int i = 0; i < n; ++i) {
big.insert(i);
}
for (int k = 0; k < n; ++k) {
int i = ord[k];
big.erase(i);
auto it = small.upper_bound(i);
int y = *it;
int x = *prev(it);
if (x != -1) {
int z = *prev(big.lower_bound(x));
res -= (ll)(y - i) * (x - z);
}
small.insert(i);
}
cout << res << '\n';
}
return 0;
}