解题关键:线段树合并模板题。线段树合并的题目一般都是权值线段树,因为结构相同,求逆序对时,遍历权值线段树的过程就是遍历所有mid的过程,所有能求出所有逆序对。
#include<iostream> #include<cstdio> #define ll long long using namespace std; int n,sz,seg; ll ans,cnt1,cnt2; int v[400005],l[400005],r[400005],root[400005]; int sum[4000005],ls[4000005],rs[4000005]; //动态开点线段树 //int new_node(){ return ++sz; } void readtree(int x){ scanf("%d",&v[x]); if(!v[x]){ l[x]=++sz; readtree(l[x]); r[x]=++sz; readtree(r[x]); } } void pushup(int k){ sum[k]=sum[ls[k]]+sum[rs[k]]; } void build(int &k,int l,int r,int val){ if(!k)k=++seg; if(l==r){sum[k]=1;return;} int mid=(l+r)>>1; if(val<=mid)build(ls[k],l,mid,val); else build(rs[k],mid+1,r,val); pushup(k); } int merge(int x,int y){ if(!x)return y; if(!y)return x; cnt1+=(ll)sum[rs[x]]*sum[ls[y]]; cnt2+=(ll)sum[ls[x]]*sum[rs[y]]; ls[x]=merge(ls[x],ls[y]); rs[x]=merge(rs[x],rs[y]); pushup(x); return x; } void solve(int x){ if(!x)return; solve(l[x]); solve(r[x]); if(!v[x]){ cnt1=cnt2=0; root[x]=merge(root[l[x]],root[r[x]]); ans+=min(cnt1,cnt2); } } int main(){ scanf("%d",&n); ++sz; readtree(1); for(int i=1;i<=sz;i++) if(v[i])build(root[i],1,n,v[i]); solve(1); printf("%lld",ans); return 0; }