双端队列
双端队列
给定一个长度为 \(n\) 的序列 \(a_1,a_2,…,a_n\) 和一个空的双端队列。
你需要按顺序将序列中的每一个数放进双端队列中,你可以选择将数插入到双端队列的队首或队尾。
你需要最小化最终得到的序列的逆序对数量。
输入格式
第一行一个整数 \(n\) , 表示序列的长度。\((1≤n≤2×10^5)\)
第二行 \(n\) 个数字 \(a_1,a_2,…,a_n\) ,表示给定的序列。 \((−10^9≤a_i≤10^9)\)。
输出格式
输出一行一个整数,表示最少的逆序对数。
样例输入
4
3 7 5 5
样例输出
2
题目分析
考虑每插入一个数对总逆序对数量的影响。
- 把 \(a_i\) 从双端队列的队头入队,则产生逆序对数量为原队列中所有比 \(a_i\) 小的数的数量。
- 把 \(a_i\)从双端队列的队尾入队,则产生逆序对数量为原队列中所有比 \(a_i\) 大的数的数量。
于是就可以贪心, \(a_i\) 以哪种方式入队所产生的逆序对数量少,就将总逆序对数量加上它,使用线段树或树状数组计算逆序对数量。
AC code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+7;
ll p[N];
ll a[N],n;
ll b[N];
int tree[N];
int lowbit(int k) {
return k & -k;
}
void add(int x,int k) {
while(x<=n) {
tree[x]+=k;
x+=lowbit(x);
}
}
int sum(int x) {
int ans=0;
while(x!=0) {
ans+=tree[x];
x-=lowbit(x);
}
return ans;
}
int main() {
int tc;
cin >> tc;
while(tc--) {
cin >> n;
for(int i = 0 ; i <= n ; i++) {
p[i] = 0,a[i] = 0,b[i] = 0,tree[i] = 0;
}
for(int i = 1 ; i <= n ; i++) {
scanf("%lld",&p[i]);
a[i] = p[i];
}
sort(p+1,p+n+1);
for(int i = 1 ; i <= n ; i++) {
a[i] = lower_bound(p+1,p+n+1,a[i])-p;
}
ll ans = 0;
for(int i = 1 ; i <= n ; i++) {
int t1 = sum(a[i]-1);
int t2 = i-1-t1-b[a[i]];
ans +=min(t1,t2);
add(a[i],1);
b[a[i]]++;
}
cout << ans << endl;
}
}

浙公网安备 33010602011771号