CF1579E2 Array Optimization by Deque

题意

一个 nn 个元素的序列和一个双端队列,按顺序每次在前或后面插入每个 aia_i,求出插入完成后最小逆序对数量。

解法

比较容易的贪心。

贪心,就是局部最优导致全局最优。显然,每次插入 aia_i,计算在前面和后面插入造成的逆序对数量,取较小的累加即可。

手玩一下样例,发现是对的。那么如何证明呢?

显然,在前面插入和后面插入,对逆序对数量有影响的总是整个队列的元素,也就是说,插入 aia_i 时,无论插在前面还是后面,对插入 ai+1a_{i+1} 是没有影响的,显然结论成立。

至于逆序对,提供一个新的方法。计算逆序对可以平衡树维护,因为插入 aia_i 在前面,其实就是求队列有多少个数比 aia_i 小,插入后面就是大,可以转化为求排名。

单次复杂度 O(nlogn)O(n \log n),最慢的测试点 468468 毫秒。

还有一个问题,多测如何清空平衡树。显然,每次都 memset 复杂度是 O(Nt)O(N t) 的,其中 NN 是数据范围,就是 21052 \cdot 10^5。尽管 memset 常数很小,但是清空一个 nn 个元素的数组,复杂度仍然是 O(n)O(n) 的,这时,循环更快。英文题面有这样一句话:

It is guaranteed that the sum of n over all test cases does not exceed 2 \cdot 10^5

翻译过来,所有测试的 nn 的和不超过 21052 \cdot 10^5,那么每次只清空上次的 nn 的个数,总循环次数不超过 21052 \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;
}
posted @   HappyBobb  阅读(34)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示