手套
描述
你现在有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;
}