[CF1601C] Optimal Insertion

发布时间 2023-07-26 11:56:52作者: 灰鲭鲨

Optimal Insertion

题面翻译

题目大意

给定两个序列 \(a,b\),长度分别为 \(n,m(1\leq n,m\leq 10^6)\)。接下来将 \(b\) 中的所有元素以任意方式插入序列 \(a\)任意位置,请找出一种插入方式使结果序列中的逆序对数量最小化,并输出这个最小值。

关于插入:任意方式插入任意位置的示例如下。

例如 \(a=\{1,2,3,4\},b=\{4,5,6\}\),则 \(c=\{4,\underline1,5,\underline2,\underline3,\underline4,6\},\{\underline1,\underline2,6,5,\underline3,4,\underline4\}\dots\) 均为合法的插入方式。但你不能修改 \(a\) 的顺序。

输入格式

本题多测(注意多测不清空爆零两行泪

第一行给定一个正整数 \(t\ (1\leq t\leq 10^4)\) 表示数据组数.

接下来对于每组数据,第一行两个整数 \(n,m\ (1\leq n,m\leq 10^6)\) 分别表示 \(a,b\) 的长度。

第二行包括 \(n\) 个整数,表示 \(a\)

第三行包括 \(m\) 个整数,表示 \(b\)

保证 \(1\leq a_i,b_i\leq 10^9,\ 1\leq \sum n,\sum m\leq 10^6\)

输出格式

对于每组数据一行一个整数,表示最小逆序对数。

题目描述

You are given two arrays of integers $ a_1, a_2, \ldots, a_n $ and $ b_1, b_2, \ldots, b_m $ .

You need to insert all elements of $ b $ into $ a $ in an arbitrary way. As a result you will get an array $ c_1, c_2, \ldots, c_{n+m} $ of size $ n + m $ .

Note that you are not allowed to change the order of elements in $ a $ , while you can insert elements of $ b $ at arbitrary positions. They can be inserted at the beginning, between any elements of $ a $ , or at the end. Moreover, elements of $ b $ can appear in the resulting array in any order.

What is the minimum possible number of inversions in the resulting array $ c $ ? Recall that an inversion is a pair of indices $ (i, j) $ such that $ i < j $ and $ c_i > c_j $ .

输入格式

Each test contains multiple test cases. The first line contains the number of test cases $ t $ ( $ 1 \leq t \leq 10^4 $ ). Description of the test cases follows.

The first line of each test case contains two integers $ n $ and $ m $ ( $ 1 \leq n, m \leq 10^6 $ ).

The second line of each test case contains $ n $ integers $ a_1, a_2, \ldots, a_n $ ( $ 1 \leq a_i \leq 10^9 $ ).

The third line of each test case contains $ m $ integers $ b_1, b_2, \ldots, b_m $ ( $ 1 \leq b_i \leq 10^9 $ ).

It is guaranteed that the sum of $ n $ for all tests cases in one input doesn't exceed $ 10^6 $ . The sum of $ m $ for all tests cases doesn't exceed $ 10^6 $ as well.

易证,\(b\) 数组一定是排序后最优。

如果 \(b\) 中存在 \(x\)\(y\) 满足 \(b_x>b_y\)\(x<y\),那么交换后无论是 \(b\) 中的还是同时存在 \(a\)\(b\) 中的逆序对都会减少。

现在看作把 \(a\) 数组插入 \(b\) 数组。

如果不管 \(a\) 的顺序的话,一定是插入到 \(b\) 中的最小的比 \(a_i\) 大的数的附近。但是 \(a\) 要按顺序插入,设 \(c_i\)\(a_i\) 需要插入的地方。

如果一个 \(c_{i}\) 在之前插入的所有地方的后面,那么就可以直接插入。否则就需要考虑怎么处理最优。

\(ls\) 为之前插入的地方的最后面,那么 \(a_i\) 可以跟着 \(ls\) 往前移动,此时只要不会撞到其他值,那么 \(ls\) 的逆序对数量减 \(1\)\(i\) 的加一,刚好抵消。

这时我们可以假设 \(i\) 已经跟着 \(ls\) 跑到了 \(c_i\) 处。难道不怕撞到其他 \(a_i\) 吗?这里的移动是假的。我们可以拿一棵大根堆维护答案,然后大根堆会把我们撞到的 \(i\) 处理掉。

对于有一大段相同的 \(b\),要处理他移动时放在所有相同的数的右端点,同时匹配时放在左端点。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int t,n,m,a[N],b[N],tr[N],lsh[N];
long long ans;
priority_queue<int>q;
int read()
{
	int s=0;
	char ch=getchar();
	while(ch<'0'||ch>'9')
		ch=getchar();
	while(ch>='0'&&ch<='9')
		s=s*10+ch-48,ch=getchar();
	return s;
}
void upd(int x,int y)
{
	for(;x<=n;x+=x&-x)
		tr[x]+=y;
}
int qry(int x)
{
	int ret=0;
	for(;x;x-=x&-x)
		ret+=tr[x];
	return ret;
}
int main()
{
	t=read();
	while(t--)
	{
		n=read(),m=read();
		for(int i=1;i<=n;i++)
			lsh[i]=a[i]=read(),tr[i]=0;
		for(int i=1;i<=m;i++)
			b[i]=read();
		sort(b+1,b+m+1);
		sort(lsh+1,lsh+n+1);
		while(!q.empty())
			q.pop();
		ans=0;
		for(int i=1;i<=n;i++)
		{
			int k=lower_bound(lsh+1,lsh+n+1,a[i])-lsh;
			upd(k,1);
			ans+=qry(k);
		}
		ans=1LL*n*(n+1)/2-ans;
		for(int i=1;i<=n;i++)
		{
			int l=lower_bound(b+1,b+m+1,a[i])-b,r=upper_bound(b+1,b+m+1,a[i])-b;
			if(!q.empty()&&q.top()>r)
			{
				ans+=q.top()-r;
				q.pop();
				q.push(r);
			}
			q.push(l);
		}
		printf("%lld\n",ans);
	}
}