[bzoj3702]二叉树

来源:互联网 发布:淘宝卖蜂蜜需要什么 编辑:程序博客网 时间:2024/06/05 19:08

题目大意

有一个n个叶子结点的树,叶子结点上有权值,且为[1,n]的排列。
你可以交换任一非叶子结点的左右儿子,请最小化中序遍历后的逆序对个数。

线段树合并

显然在一个非叶子结点需要确定左右次序,并且这与其左右子树内的次序无关。
对于每个结点维护一个线段树,那么每次就是合并左右儿子的线段树。
至于如何统计每种次序的逆序对个数,线段树合并的时候统计就好了,具体见代码。

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;typedef long long ll;const int maxn=200000+10,maxd=200000*20+10;int root[maxn*2],sum[maxd],left[maxd],right[maxd],tree[maxn*2][2];int i,j,k,l,t,n,m,tot,top;ll ans,cnt1,cnt2;void insert(int &x,int l,int r,int a){    if (!x) x=++tot;    if (l==r){        sum[x]++;        return;    }    int mid=(l+r)/2;    if (a<=mid) insert(left[x],l,mid,a);else insert(right[x],mid+1,r,a);    sum[x]=sum[left[x]]+sum[right[x]];}int merge(int x,int y,int l,int r){    if (!x||!y) return x+y;    if (l==r){        sum[x]+=sum[y];        return x;    }    int mid=(l+r)/2;    cnt1+=(ll)sum[left[x]]*sum[right[y]];    cnt2+=(ll)sum[right[x]]*sum[left[y]];    left[x]=merge(left[x],left[y],l,mid);    right[x]=merge(right[x],right[y],mid+1,r);    sum[x]=sum[left[x]]+sum[right[x]];    return x;}void dfs(int x){    scanf("%d",&t);    if (t) insert(root[x],1,n,t);    else{        tree[x][0]=++top;        dfs(tree[x][0]);        tree[x][1]=++top;        dfs(tree[x][1]);        cnt1=cnt2=0;        root[x]=merge(root[tree[x][0]],root[tree[x][1]],1,n);        ans+=min(cnt1,cnt2);    }}int main(){    scanf("%d",&n);    dfs(top=1);    printf("%lld\n",ans);}
0 0