POI2011]ROT-Tree Rotations,洛谷P35231,線段樹合併
阿新 • • 發佈:2018-11-02
正題
給一顆n各節點的二叉樹,每個節點可以交換左右子樹,求先序遍歷的最小值。
這題很明顯,交換兩棵子樹,子樹內的逆序對不變的,變的只是左右兩邊出現的逆序對,那麼每一個葉子節點開一棵權值線段樹,每次向上合併計算答案就可以了。
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> using namespace std; int n; long long ans=0; int root[400010]; int T[10000010]; int ls[10000010],rs[10000010]; int v; int tot=0; int len=0; long long res=0,res2=0; void insert(int &now,int l,int r){ now=++tot; T[now]++; if(l==r) return ; int mid=(l+r)/2; if(v<=mid) insert(ls[now],l,mid); else insert(rs[now],mid+1,r); } int merge(int x,int y,int l,int r){ if(x==0) return y; if(y==0) return x; if(l==r) { T[x]+=T[y]; return x; } res+=(long long)T[rs[x]]*T[ls[y]]; res2+=(long long)T[ls[x]]*T[rs[y]]; int mid=(l+r)/2; ls[x]=merge(ls[x],ls[y],l,mid); rs[x]=merge(rs[x],rs[y],mid+1,r); T[x]=T[ls[x]]+T[rs[x]]; return x; } void solve(){ int x=++len; int d; scanf("%d",&d); if(d!=0) { v=d; insert(root[x],1,n); return ; } int a=len+1;solve(); int b=len+1;solve(); res=0;res2=0;root[x]=merge(root[a],root[b],1,n); ans+=min(res,res2); } int main(){ scanf("%d",&n); solve(); printf("%lld\n",ans); }
這種打法是可以支援可重元素的。