bzoj 2212 : [Poi2011]Tree Rotations (线段树合并)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2212

思路:用线段树合并求出交换左右儿子之前之后逆序对的数量,如果数量变小则交换.

实现代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int M = 4e5+10;
int n,cnt,idx;
ll ans,cnt1,cnt2;
int v[M],l[M],r[M],root[M];
int sum[M*10],ls[M*10],rs[M*10];
void init_tree(int x){
    scanf("%d",&v[x]);
    if(!v[x]){
        l[x] = ++cnt;
        init_tree(l[x]);
        r[x] = ++cnt;
        init_tree(r[x]);
    }
}

void pushup(int rt){
    sum[rt] = sum[ls[rt]] + sum[rs[rt]];
}

void build(int p,int l,int r,int &rt){
    if(!rt) rt = ++idx;
    if(l == r){
        sum[rt] = 1;
        return ;
    }
    int mid = (l + r) >> 1;
    if(p <= mid) build(p,l,mid,ls[rt]);
    else build(p,mid+1,r,rs[rt]);
    pushup(rt);
}

int merge(int x,int y){
    if(!x) return y;
    if(!y) return x;
    cnt1 += (ll)sum[rs[x]]*sum[ls[y]];
    cnt2 += (ll)sum[ls[x]]*sum[rs[y]];
    ls[x] = merge(ls[x],ls[y]);
    rs[x] = merge(rs[x],rs[y]);
    pushup(x);
    return x;
}

void solve(int x){
    if(!x) return ;
    solve(l[x]); solve(r[x]);
    if(!v[x]){
        cnt1 = cnt2 = 0;
        root[x] = merge(root[l[x]],root[r[x]]);
        ans += min(cnt1,cnt2);
    }
}

int main()
{
    scanf("%d",&n);
    cnt = 1;
    init_tree(1);
    for(int i = 1;i <= cnt;i ++){
        if(v[i])
            build(v[i],1,n,root[i]);
    }
    solve(1);
    printf("%lld\n",ans);
    return 0;
}

 

posted @ 2018-08-28 19:03  冥想选手  阅读(137)  评论(0编辑  收藏  举报