[bzoj3702]二叉树

题目大意

有一个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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值