解题思路
线段树合并,考虑交换两个子树时,对除这两棵子树外的其余点的逆序对不会造成影响,所以我们只需要贪心的使这两棵子树产生的逆序对最小。而考虑时我们也只需要考虑两棵子树间的逆序对数,不需要考虑每棵子树内部逆序对数,这样就非常好算了,可以线段树合并,合并的同时统计一下交换前和后的逆序对数,然后取个(min)加到答案里。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int N=200005;
typedef long long LL;
inline int rd(){
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) f=ch=='-'?9:1,ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return f?x:-x;
}
int n,tot,sum[N<<5],ls[N<<5],rs[N<<5];
LL ans,res1,res2;
int update(int l,int r,int pos){
int now=++tot; sum[now]=1;
if(l==r) return now; int mid=(l+r)>>1;
if(pos<=mid) ls[now]=update(l,mid,pos);
else rs[now]=update(mid+1,r,pos);
sum[now]=sum[ls[now]]+sum[rs[now]];
return now;
}
int merge(int u,int v,int l,int r){
if(!u || !v) return (u|v);
if(l==r) {sum[++tot]=sum[u]+sum[v]; return tot;}
int mid=(l+r)>>1;
res1+=(LL)sum[rs[u]]*sum[ls[v]],res2+=(LL)sum[ls[u]]*sum[rs[v]];
ls[u]=merge(ls[u],ls[v],l,mid); rs[u]=merge(rs[u],rs[v],mid+1,r);
sum[u]+=sum[v]; return u;
}
int DFS(){
int now=rd(),tmp,LS,RS;
if(now) return update(1,n,now);
LS=DFS(); RS=DFS(); tmp=merge(LS,RS,1,n);
ans+=min(res1,res2); res1=res2=0;
return tmp;
}
int main(){
n=rd(); DFS();
printf("%lld",ans);
return 0;
}