CF1579E2 Array Optimization by Deque
题意
一个 个元素的序列和一个双端队列,按顺序每次在前或后面插入每个 ,求出插入完成后最小逆序对数量。
解法
比较容易的贪心。
贪心,就是局部最优导致全局最优。显然,每次插入 ,计算在前面和后面插入造成的逆序对数量,取较小的累加即可。
手玩一下样例,发现是对的。那么如何证明呢?
显然,在前面插入和后面插入,对逆序对数量有影响的总是整个队列的元素,也就是说,插入 时,无论插在前面还是后面,对插入 是没有影响的,显然结论成立。
至于逆序对,提供一个新的方法。计算逆序对可以平衡树维护,因为插入 在前面,其实就是求队列有多少个数比 小,插入后面就是大,可以转化为求排名。
单次复杂度 ,最慢的测试点 毫秒。
还有一个问题,多测如何清空平衡树。显然,每次都 memset 复杂度是 的,其中 是数据范围,就是 。尽管 memset 常数很小,但是清空一个 个元素的数组,复杂度仍然是 的,这时,循环更快。英文题面有这样一句话:
It is guaranteed that the sum of n over all test cases does not exceed 2 \cdot 10^5
翻译过来,所有测试的 的和不超过 ,那么每次只清空上次的 的个数,总循环次数不超过 。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <unordered_map>
#include <climits>
using namespace std;
#define int long long
const int N = 2e5 + 5, INF = INT_MAX;
unordered_map<int, int> mp;
int t, n;
int idx, root;
class Treap
{
public:
struct Node
{
int l, r, val, cnt, sz, key;
}tr[N];
void pushup(int u)
{
tr[u].sz = tr[tr[u].l].sz + tr[tr[u].r].sz + tr[u].cnt;
}
void zig(int& x)
{
int q = tr[x].l;
tr[x].l = tr[q].r, tr[q].r = x, x = q;
pushup(tr[x].r), pushup(x);
}
void zag(int& x)
{
int q = tr[x].r;
tr[x].r = tr[q].l, tr[q].l = x, x = q;
pushup(tr[x].l), pushup(x);
}
int get_node(int key)
{
tr[++idx].key = key;
tr[idx].sz = tr[idx].cnt = 1;
tr[idx].val = rand();
return idx;
}
void build()
{
for (register int i(1); i <= idx; ++i) tr[i] = { 0, 0, 0, 0, 0, 0 };
idx = 0;
get_node(-INF), get_node(INF);
root = 1;
tr[1].r = 2;
pushup(root);
if (tr[1].val < tr[2].val) zag(root);
}
void insert(int& x, int key)
{
if (!x) x = get_node(key);
else if (tr[x].key == key) tr[x].cnt++;
else if (tr[x].key > key)
{
insert(tr[x].l, key);
if (tr[tr[x].l].val > tr[x].val) zig(x);
}
else
{
insert(tr[x].r, key);
if (tr[tr[x].r].val > tr[x].val) zag(x);
}
pushup(x);
}
int get_rank(int x, int key)
{
if (!x) return 0;
if (tr[x].key == key) return tr[tr[x].l].sz + 1;
if (tr[x].key > key) return get_rank(tr[x].l, key);
return tr[tr[x].l].sz + tr[x].cnt + get_rank(tr[x].r, key);
}
};
Treap tp;
signed main()
{
scanf("%lld", &t);
while (t--)
{
tp.build();
mp.clear();
int ans = 0, now = 0;
scanf("%lld", &n);
while (n--)
{
now++;
int x;
scanf("%lld", &x);
mp[x]++;
tp.insert(root, x);
int k = tp.get_rank(root, x) - 1;
ans += min(max(0ll, k - 1), max(0ll, now - (k - 1) - mp[x]));
}
printf("%lld\n", ans);
}
return 0;
}

浙公网安备 33010602011771号