1. 程式人生 > >【BZOJ2212】Tree Rotations(POI2011)-平衡樹啟發式合併

【BZOJ2212】Tree Rotations(POI2011)-平衡樹啟發式合併

測試地址:Tree Rotations
做法:本題需要用到平衡樹啟發式合併。
對於葉子節點,最優答案顯然是0。然後對於每棵子樹,我們發現由轉換它的左右子樹所多出的逆序對數,僅和兩邊都有什麼數字有關,而不和兩邊的數字順序有關,所以我們對於每個葉子節點儲存一棵平衡樹,然後在每個節點合併左右子樹的平衡樹,在合併的同時,算出新增的逆序對數,然後判斷要不要轉換子樹,選擇最小的答案累加即可。
以下是本人程式碼:

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n,tot=0;
int top[200010
],fa[200010],ch[200010][2],key[200010]; ll ans=0,siz[200010]; void pushup(int x) { siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1; } void rotate(int x,bool f) { int y=fa[x]; ch[y][!f]=ch[x][f]; fa[ch[x][f]]=y; if (fa[y]) ch[fa[y]][ch[fa[y]][1]==y]=x; fa[x]=fa[y]; fa[y]=x; ch[x][f]=y; pushup(y),pushup(x); } void
Splay(int x,int &rt,int goal) { while(fa[x]!=goal) { if (fa[fa[x]]==goal) rotate(x,ch[fa[x]][0]==x); else { int y=fa[x],z=fa[fa[x]]; bool f=(ch[y][1]==x); if (ch[z][f]==y) rotate(y,!f),rotate(x,!f); else rotate(x,!f),rotate(x,f); } } if
(!goal) rt=x; } void insert(int &v,int &rt,int x,int f,ll &newans) { if (!v) { v=x; ch[v][0]=ch[v][1]=0; fa[v]=f; siz[v]=1; Splay(v,rt,0); return; } if (key[x]>key[v]) newans+=siz[ch[v][0]]+1; insert(ch[v][key[x]>key[v]],rt,x,v,newans); } int find(int x) { int r=x,i=x,j; while(r!=top[r]) r=top[r]; while(i!=r) j=top[i],top[i]=r,i=j; return r; } ll order(int v,int &rt) { ll newans=0,lson=ch[v][0]; if (ch[v][1]) newans+=order(ch[v][1],rt); insert(rt,rt,v,0,newans); if (lson) newans+=order(lson,rt); return newans; } void merge(int x,int y) { int fx=find(x),fy=find(y),rt; ll sizx,sizy; top[fx]=fy; Splay(x,rt,0),Splay(y,rt,0); if (siz[x]>siz[y]) swap(x,y); sizx=siz[x],sizy=siz[y]; ll newans=order(x,y); ans+=min(newans,sizx*sizy-newans); } void dfs() { int x; scanf("%d",&x); if (x) { int v=++tot; top[v]=v; fa[v]=ch[v][0]=ch[v][1]=0; siz[v]=1; key[v]=x; } else { int ch1,ch2; dfs(); ch1=tot; dfs(); ch2=tot; merge(ch1,ch2); } } int main() { scanf("%d",&n); siz[0]=0; dfs(); printf("%lld",ans); return 0; }