题目分析:
首先有一个很常用并且十分显然的结论(楼楼不知道怎么证明):一串数交换后形成的以原来编号为元素的序列的逆序对数为最小交换次数,其中交换为两两交换(其实这个结论都比较能感觉到它的正确性)。
对于这道题,我们显然可以按照高度从小到大排序后枚举每个数,那么对于每个数它对答案的贡献就是剩下的数与它能够形成逆序对数的个数。
这个时候我们就可以根据题意开两个数组,其中一个维护比一个数的位置大的数的个数,另一个则反之。由于可能存在相同的数,我们在枚举的时候直接删掉就可以了。查询时我们只需要取两个当中的最小值,查询完后删除。
参考代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define SG string
#define LL long long
#define LowBit(X) (X&(-X))
#define Fp(A,B,C,D) for(A=B;A<=C;A+=D)
#define Fm(A,B,C,D) for(A=B;A>=C;A-=D)
#define Clear(A) memset(A,0,sizeof(A))
#define Copy(A,B) memcpy(A,B,sizeof(A))
using namespace std;
const LL Max=3e5+5;
const LL Mod=1e9+7;
const LL Inf=1e18;
struct Node{
LL ID,Height;
Node(){}
Node(LL X,LL Y):ID(X),Height(Y){}
void InPut(LL X,LL Y){
ID=X,Height=Y;
}
friend bool operator < (const Node X,const Node Y){
return X.Height<Y.Height;
}
}Tree[Max];
LL N,Ans,A[Max],B[Max];
inline LL Read(){
LL X=0;char CH=getchar();bool F=0;
while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
return F?-X:X;
}
inline void Write(LL X){
if(X<0)X=-X,putchar('-');
if(X>9)Write(X/10);
putchar(X%10+48);
}
void Update(LL X,LL V){
LL I,J,K;
Fp(I,X,N,LowBit(I)){
A[I]+=V;
}
Fm(I,X,1,LowBit(I)){
B[I]+=V;
}
}
LL GetAns(LL X){
LL I,J,K,Ans1=0,Ans2=0;
Fp(I,X,N,LowBit(I)){
Ans1+=B[I];
}
Fm(I,X,1,LowBit(I)){
Ans2+=A[I];
}
return min(Ans1,Ans2);
}
int main(){
LL I,J,K;
N=Read();
Fp(I,1,N,1){
Tree[I].InPut(I,Read());Update(I,1);
}sort(Tree+1,Tree+1+N);
for(I=1;I<=N;I=J){
for(J=I;Tree[J].Height==Tree[I].Height;J++){
Update(Tree[J].ID,-1);
}
for(J=I;Tree[J].Height==Tree[I].Height;J++){
Ans+=GetAns(Tree[J].ID);
}
}Write(Ans);
return 0;
}