BZOJ2212【POI2011】ROT:Tree Rotation 线段树合并
题意:
给一棵n(1≤n≤200000个叶子的二叉树,可以交换每个点的左右子树,要求叶子遍历序的逆序对最少。
分析:
求逆序对我们可以想到权值线段树,所以我们对每个点建一颗线段树(为了避免空间爆炸,采取动态开点的科技)
两个子节点可以交换,于是我们可以递归,自底向上贪心解决问题,每次线段树合并,在合并时,统计交换左右子节点后,横跨当前位置的逆序对数量,以及不交换子节点的情况下的这个数量,将更优的计入答案。这道问题就圆满解决了。
代码:
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const int N=2000005; 5 struct node{ll v;int ls,rs;}t[N*4]; 6 int n,m,tot=0,cnt=1,nm[N],ls[N],rs[N],rt[N]; 7 ll ans=0,ans1,ans2; 8 void read(int x){ 9 scanf("%d",&nm[x]); 10 if(!nm[x]) read(ls[x]=++cnt), 11 read(rs[x]=++cnt);return ; 12 } void update(int &x,int l,int r,int v){ 13 if(!x) x=++tot;int mid=l+r>>1; 14 if(l==r){t[x].v=1;return ;} 15 if(v<=mid) update(t[x].ls,l,mid,v); 16 else update(t[x].rs,mid+1,r,v); 17 t[x].v=t[t[x].rs].v+t[t[x].ls].v; 18 } int merge(int x,int y){ 19 if(!x||!y) return x|y; 20 ans1+=(ll)t[t[x].rs].v*t[t[y].ls].v; 21 ans2+=(ll)t[t[x].ls].v*t[t[y].rs].v; 22 t[x].ls=merge(t[x].ls,t[y].ls); 23 t[x].rs=merge(t[x].rs,t[y].rs); 24 t[x].v=t[t[x].ls].v+t[t[x].rs].v; 25 return x; 26 } void dfs(int x){ 27 if(!x) return ; 28 dfs(ls[x]);dfs(rs[x]); 29 if(!nm[x]){ 30 ans1=ans2=0; 31 rt[x]=merge(rt[ls[x]],rt[rs[x]]); 32 ans+=min(ans1,ans2); 33 } return ; 34 } int main(){ 35 scanf("%d",&n);read(1); 36 for(int i=1;i<=cnt;i++) 37 if(nm[i]) update(rt[i],1,n,nm[i]); 38 dfs(1);printf("%lld\n",ans); 39 return 0; 40 }