1. 程式人生 > >【BZOJ2212/Poi2011】Tree Rotations

【BZOJ2212/Poi2011】Tree Rotations

解析:

  線段樹合併。
  這裡的遍歷指的是中序遍歷。考慮對於一個節點對答案的貢獻為左右兒子單獨的貢獻加上左兒子對右兒子的貢獻或交換後左兒子對右兒子的貢獻,用線段樹合併,從葉子節點合併到根即可。

程式碼:

#include <bits/stdc++.h>
using namespace std;

const int Max=500005;
int n,m,tot,size;
long long ans,s1,s2;
int tree[Max*20],lc[Max*20],rc[Max*20],rt[Max],a[Max],l[Max],r[Max];

inline int get_int()
{ int x=0,f=1;char c; for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar()); if(c=='-') f=-1,c=getchar(); for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0'; return x*f; } inline void build(int x) { a[x]=get_int(); if(!a[x]) { l[x]=++size; build(l[x]); r[x]=++size;
build(r[x]); } } inline void add(int &now,int l,int r,int x) { if(!now) now=++tot; tree[now]++; if(l==r) return; int mid=(l+r)>>1; if(x<=mid) add(lc[now],l,mid,x); else add(rc[now],mid+1,r,x); } inline int merge(int rt1,int rt2,int l,int r) { if(!rt1||!rt2) return rt1+rt2; tree[
rt1]+=tree[rt2]; if(l==r)return rt1; s1+=static_cast<long long>(tree[rc[rt1]])*tree[lc[rt2]]; s2+=static_cast<long long>(tree[rc[rt2]])*tree[lc[rt1]]; int mid=l+r>>1; lc[rt1]=merge(lc[rt1],lc[rt2],l,mid),rc[rt1]=merge(rc[rt1],rc[rt2],mid+1,r); return rt1; } inline void solve(int p) { if(!p) return; solve(l[p]),solve(r[p]); if(!a[p]) { s1=s2=0; rt[p]=merge(rt[l[p]],rt[r[p]],1,n); ans+=min(s1,s2); } } int main() { n=get_int(),size++; build(1); for(int i=1;i<=size;i++) if(a[i]) add(rt[i],1,n,a[i]); solve(1); cout<<ans<<"\n"; return 0; }