天天看点

树状数组求逆序对 - 手套

手套

描述

你现在有N对手套,但是你不小心把它们弄乱了,需要把它们整理一下。N对手套被一字排开,每只手套都有一个颜色,被记为0~N-1,你打算通过交换把每对手套都排在一起。由于手套比较多,你每次只能交换相邻两个手套。请你计算最少要交换几次才能把手套排整齐。

输入

输入第一行一个N,表示手套对数。

第二行有2N个整数,描述了手套的颜色。每个数都在0~N-1之间,且每个数字都会出现恰好两次。

输出

一行,包含一个数,表示最少交换次数。

样例输入

2

0 1 0 1

样例输出

1

数据范围

30%的数据N≤9;

60%的数据N≤1000;

100%的数据N≤200,000。

Analysis

树状数组–>如果数据范围过大的话就需要离散化,但总的来说还是很好写的

归并排序–>懒得写……

这种类型的题一定要get到,只能允许相邻的两个点交换,就可以逆序对了

注意不可以在原数组上求逆序对,因为1 0 1 0和0 1 0 1其实是一致的

而且我们有点类似贪心,是保证在前面的数不会被交换到后面去

最后注意一点:要开long long(手动算一下极端情况的逆序对个数: n ∗ ( n + 1 ) 2 \frac{n*(n+1)}{2} 2n∗(n+1)​)

Code

#include<bits/stdc++.h>
#define in read()
#define N 300000
#define ll long long 
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9') {
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
int n,id[N],cnt=0;
ll t[N];
inline ll lowbit(int x){return x&(-x);}
inline void insert(int pos){
	while(pos<=n){
		t[pos]++;
		pos+=lowbit(pos);
	}
}
inline ll query(int pos){
	ll ans=0;
	while(pos){
		ans+=t[pos];
		pos-=lowbit(pos);
	}
	return ans;
}
int main(){
	n=in;
	int i,j;
	ll ans=0;
	for(i=1;i<=2*n;++i){
		j=in;
		if(!id[j])	id[j]=++cnt;
		insert(id[j]);
		ans+=i-query(id[j]);
	}
	cout<<ans;
	return 0;
}