發現x的子樹在後續處理中不會影響逆序對的狀況(只關心有哪些值,相對位置已經不重要了)c++
f[x]表示x爲根的子樹最小逆序對數git
考慮左右兒子交換與否。ui
暴力是O(n^2)的spa
考慮線段樹合併code
左右兒子線段樹合併的時候,直接相似於分治,記錄另外一棵數小於某個數的個數,blog
選擇代價小的放在前面get
具體看代碼:it
1.x,y別寫錯。。。class
2.開long long,並且線段樹可能存在一個點的sz大於n,乘起來會爆intdi
#include<bits/stdc++.h> #define reg register int #define mid ((l+r)>>1) #define il inline #define numb (ch^'0') using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=200000+5; int n,ch[2*N][2]; struct tr{ int ls,rs; int sz; }t[40*N]; int tot; void pushup(int x){ t[x].sz=t[t[x].ls].sz+t[t[x].rs].sz; } ll xfsz,yfsz; int rt[2*N],m; ll f[2*N]; int merge(int x,int y,int l,int r){ if(!x||!y) return x+y; if(l==r){ t[x].sz+=t[y].sz; return x; } xfsz+=(ll)t[t[x].rs].sz*t[t[y].ls].sz; yfsz+=(ll)t[t[y].rs].sz*t[t[x].ls].sz; t[x].ls=merge(t[x].ls,t[y].ls,l,mid); t[x].rs=merge(t[x].rs,t[y].rs,mid+1,r); pushup(x); return x; } void upda(int &x,int l,int r,int p){ if(!x) x=++tot; if(l==r){ t[x].sz=1; return; } if(p<=mid) upda(t[x].ls,l,mid,p); else upda(t[x].rs,mid+1,r,p); pushup(x); } void build(int &x){ x=++m; int val;rd(val); if(val==0){ build(ch[x][0]);build(ch[x][1]); }else{ upda(rt[x],1,n,val); } } void dfs(int x){ if(ch[x][0]&&ch[x][1]){ dfs(ch[x][0]),dfs(ch[x][1]); // cout<<" back to "<<x<<endl; xfsz=0;yfsz=0; rt[x]=rt[ch[x][0]]; rt[x]=merge(rt[x],rt[ch[x][1]],1,n); f[x]=f[ch[x][0]]+f[ch[x][1]]+min(xfsz,yfsz); // cout<<" xfsz "<<xfsz<<" yfsz "<<yfsz<<endl; // cout<<" f[x] "<<f[x]<<endl; } } int main(){ rd(n); int haha; build(haha); dfs(1); printf("%lld\n",f[1]); return 0; } } signed main(){ // freopen("4.in","r",stdin); // freopen("4.out","w",stdout); Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/2/10 11:57:09 */