双端队列

双端队列

给定一个长度为 \(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

题目分析

考虑每插入一个数对总逆序对数量的影响。

  1. \(a_i\) 从双端队列的队头入队,则产生逆序对数量为原队列中所有比 \(a_i\) 小的数的数量。
  2. \(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;
	}
}
posted @ 2022-06-05 15:06  seekerHeron  阅读(95)  评论(0)    收藏  举报