题目
事实上,问题 E1 和 E2 没有太多共同点。您可能应该将它们视为两个独立的问题。
给定一个整数数组 a[1…n]=[a1,a2,…,an]。
让我们考虑一个空的双端队列(双端队列)。 deque 是一种数据结构,支持在开头和结尾添加元素。因此,如果双端队列中当前有元素 [3,4,4],则在开头添加元素 1 将生成序列 [1,3,4,4],将相同元素添加到末尾将生成 [ 3,4,4,1]。
数组的元素依次添加到最初为空的双端队列中,从 a1 开始,以 an 结束。在将每个元素添加到双端队列之前,您可以选择是将其添加到开头还是结尾。
例如,如果我们考虑一个数组 a=[3,7,5,5],可能的动作序列之一如下所示:
1.在deque的开头加3:deque里面有一个序列[3];
2.在deque的末尾加7:deque里面有一个序列[3,7];
3.在deque的末尾加5:deque里面有一个序列[3,7,5];
4.在deque开头加5:deque里面有一个序列[5,3,7,5];
处理整个数组后,在双端队列中找到最小可能的反转次数。
序列 d 中的反转是一对索引 (i,j),使得 i<j 和 di>dj。例如,数组 d=[5,3,7,5] 正好有两个反转 - (1,2) 和 (3,4),因为 d1=5>3=d2 和 d3=7>5=d4。
输入
第一行包含一个整数 t (1≤t≤1000)——测试用例的数量。
接下来的 2t 行包含测试用例的描述。
每个测试用例描述的第一行包含一个整数 n (1≤n≤2⋅105) — 数组大小。描述的第二行包含 n 个空格分隔的整数 ai (−109≤ai≤109) — 数组的元素。
保证所有测试用例的 n 总和不超过 2⋅105。
输出
打印 t 行,每行包含对应测试用例的答案。测试用例的答案应该是一个整数——执行所描述的算法后双端队列中可能的最小反转次数。
题解思路
树状数组求逆序数中 树状数组存的是某点出现的次数,即类似桶排序一样,再用快速求前缀和来求出大于这个部分和小于这个部分的所有数,数据是1e9
,所以我们要进行离散化,排序后去重,hash出这个数在正常数列的下标一样的存在(因为排序了。我们只需比较下标就能知道大小关系了)。
这样直接跑树状数组求逆序数的板子就行了。
因为这个数加前面就相当于求之前加入的数中有多少小于这个数
加后面则相反。
比较大小取min即可。
为什么这样求能得出最优解呢?
抖腿哥证明了参考视频
有关离散化操作的问题
一开始使用的是unordered_map,发生了hash冲突使得查询复杂度从O1变成On就TLE了第19个样例。
改成map就过了。
unordered_map 底层是hash函数 , 在 大于1e8的时候还是容易出问题的。
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
#include <map>
#include <string>
#include <unordered_map>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 200010 ;
long long tre[ N ] ;
long long a[ N ] ;
long long b[ N ] ;
int n;
int lbit(int x )
{
return x & ( -x );
}
void xg(int x , int y )
{
for (int i = x ; i <= n ; i += lbit(i) )
tre[i] += y;
}
long long sum(int x )
{
long long ans = 0 ;
for (int i = x ; i ; i -= lbit(i) )
ans += tre[i];
return ans;
}
int main ()
{
ios::sync_with_stdio(false);
int T;
cin >> T;
while(T--)
{
map <long long , int > h ;
long long ans = 0 ;
int pit = 1 ;
cin >> n ;
for (int i = 1 ; i <= n ; i++ )
{
cin >> a[i] ;
b[i] = a[i] ;
}
sort(b+1 , b + 1 + n );
for (int i = 1 ; i <= n ; i++ )
{
if(!h[b[i]]){
h[b[i]] = pit;
pit++;
}
}
for (int i = 1 ; i <= pit ; i++ )
tre[i] = 0 ;
pit--;
for (int i = 1 ; i <= n ; i++ )
{
int y = h[a[i]];
long long t1 = sum(pit) - sum(y) ;
long long t2 = sum(y-1);
ans += min(t1,t2);
xg( y , 1 );
}
cout<<ans<<"\n";
}
return 0 ;
}