BZOJ 2212 [Poi 2011] 线段树合并 解题报告

来源:互联网 发布:少女漫画软件下载 编辑:程序博客网 时间:2024/06/05 09:56

2212: [Poi2011]Tree Rotations

Description

现在有一棵二叉树,所有非叶子节点都有两个孩子。在每个叶子节点上有一个权值(有n个叶子节点,满足这些权值为1..n的一个排列)。可以任意交换每个非叶子节点的左右孩子。
要求进行一系列交换,使得最终所有叶子节点的权值按照遍历序写出来,逆序对个数最少。

Input

第一行n
下面每行,一个数x
如果x==0,表示这个节点非叶子节点,递归地向下读入其左孩子和右孩子的信息,
如果x!=0,表示这个节点是叶子节点,权值为x
1<=n<=200000

Output

一行,最少逆序对个数

Sample Input

3
0
0
3
1
2

Sample Output

1

【解题报告】
首先我们要知道,一个节点的左右子树的子树是否交换过对这个节点的逆序对数目没有影响
每个节点的逆序对是 左子树的逆序对的数量+右子树的逆序对数量+跨越子树的逆序对数量
交换子树更改的只是最后那个跨越子树的逆序对数量。
如果我们交换了左右子树,跨越子树的逆序对数量为没交换时左子树中

/**************************************************************    Problem: 2212    User: onepointo    Language: C++    Result: Accepted    Time:10536 ms    Memory:224276 kb****************************************************************/#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define N 8000010#define LL long longLL ans=0,ans1=0,ans2=0;  int n,r[N],root,ls[N],rs[N];int a[N],s[N][2],ind=0,t[N];void build(int &rt){    rt=++ind;scanf("%d",&a[rt]);    if(a[rt]) return;    build(ls[rt]);    build(rs[rt]);}void pushup(int rt){    t[rt]=t[s[rt][0]]+t[s[rt][1]];}void insert(int &rt,int l,int r,int pos){    if(!rt) rt=++ind;      if(l==r) {t[rt]=1;return;}      int m=(l+r)>>1;      if(pos<=m) insert(s[rt][0],l,m,pos);      else insert(s[rt][1],m+1,r,pos);      pushup(rt);  }int merge(int x,int y){    if(!x) return y;    if(!y) return x;    ans1+=(LL)t[s[x][1]]*t[s[y][0]];      ans2+=(LL)t[s[x][0]]*t[s[y][1]];      s[x][0]=merge(s[x][0],s[y][0]);      s[x][1]=merge(s[x][1],s[y][1]);      pushup(x);return x;  }void solve(int x){      if(a[x]) return;      solve(ls[x]);solve(rs[x]);      ans1=ans2=0;    r[x]=merge(r[ls[x]],r[rs[x]]);      ans+=min(ans1,ans2);  }  int main(){      scanf("%d",&n);      build(root);      for(int i=1;i<=ind;++i)    {        if(a[i])             insert(r[i],1,n,a[i]);    }      solve(root);      printf("%lld\n",ans);    return 0;}
原创粉丝点击