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 }
线段树合并

 

posted @ 2019-02-17 16:40  杜宇一声  阅读(170)  评论(0编辑  收藏  举报