[CF1601C] Optimal Insertion
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);
}
}