BZOJ2212: [Poi2011]Tree Rotations 线段树合并

2212: [Poi2011]Tree Rotations
Time Limit: 20 Sec Memory Limit: 259 MB
Submit: 1501 Solved: 596
[Submit][Status][Discuss]
Description

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

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

1<=n<=200000

Output

In the first and only line of the standard output a single integer is to be printed: the minimum number of inversions in the corona of the input tree that can be obtained by a sequence of rotations.

一行,最少逆序对个数

Sample Input
3
0
0
3
1
2

Sample Output
1

题解:
首先考虑树上某一个节点,这个节点的左子树与右子树对答案的贡献只有两种情况要考虑——交换或不交换,至于两棵子树内部怎么搞,和这一部分是没有关系的。
考虑启发式合并来维护每个子树… O(nlog2n) O ( n l o g 2 n ) ,T啦
这个时候就要请出线段树合并了。考虑维护每个子树的权值线段树。这个权值线段树显然是可以动态开点并且合并的。考虑权值线段树上,每个点左右子树对两种情况答案的贡献:将左子树上权值线段树的点归于集合1,右子树归于集合2,权值线段树上某一点的左右子树对答案1(不交换)的贡献为:左子树中的二号点个数 * 右子树中的一号点个数;对答案2(交换)的贡献为:左子树中的一号点个数 * 右子树中的二号点个数。
这个点的左右子树对这个点答案的贡献为min(答案1,答案2)+左右儿子的答案。
根节点的答案即为要求的答案。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
using std::cin;
using std::cout;
using std::min;
using std::endl;
const int maxn = 200000 + 5;
struct node {
    int msg,lson,rson,first;
    long long ans;
} tree[maxn * 19];
node M_tree[maxn * 19];
int first[maxn * 19];
int n;
int root;
int cnt;
int build() {
    int step = ++cnt;
    int x;
    scanf("%d",&x);
    if(x != 0) {
        tree[step].msg = x;
    }
    else {
        tree[step].lson = build();
        tree[step].rson = build();
    }
    return step;
}
int add(int l,int r,int pos) {
    int step = ++cnt;
    if(l == r) {
        M_tree[step].msg = 1;
        return step;
    }
    int mid = (l + r) / 2;
    if(pos <= mid)
        M_tree[step].lson = add(l , mid , pos);
    else
        M_tree[step].rson = add(mid + 1 , r , pos);
    M_tree[step].msg = 1;
    return step;
}
long long ans1 , ans2;
int merge(int step1,int step2,int l,int r) {
    int step = ++cnt;
    M_tree[step].msg = M_tree[step1].msg + M_tree[step2].msg;
    if(l == r) return step;
    ans1 += (long long)M_tree[M_tree[step1].lson].msg * (long long)M_tree[M_tree[step2].rson].msg;
    ans2 += (long long)M_tree[M_tree[step2].lson].msg * (long long)M_tree[M_tree[step1].rson].msg;
    int mid = (l + r) >> 1;
    if(M_tree[step1].lson && M_tree[step2].lson) M_tree[step].lson = merge(M_tree[step1].lson , M_tree[step2].lson , l , mid);
    else if(M_tree[step1].lson) M_tree[step].lson = M_tree[step1].lson;
    else if(M_tree[step2].lson) M_tree[step].lson = M_tree[step2].lson;
    if(M_tree[step1].rson && M_tree[step2].rson) M_tree[step].rson = merge(M_tree[step1].rson , M_tree[step2].rson , mid + 1 , r);
    else if(M_tree[step1].rson) M_tree[step].rson = M_tree[step1].rson;
    else if(M_tree[step2].rson) M_tree[step].rson = M_tree[step2].rson;

    return step;
}
void dfs(int step) {
    if(tree[step].msg != 0) {
        tree[step].first = add(1 , n , tree[step].msg);
        return;
    }
    if(tree[step].lson) dfs(tree[step].lson);
    if(tree[step].rson) dfs(tree[step].rson);
    if(tree[step].lson != 0 && tree[step].rson == 0) {
        tree[step].ans = tree[tree[step].lson].ans;
        tree[step].first = tree[tree[step].lson].first;
        return;
    }
    if(tree[step].rson != 0 && tree[step].lson == 0) {
        tree[step].ans = tree[tree[step].rson].ans;
        tree[step].first = tree[tree[step].rson].first;
        return;
    }
    ans1 = 0;
    ans2 = 0;
    tree[step].first = merge(tree[tree[step].lson].first , tree[tree[step].rson].first , 1 , n);
    tree[step].ans += min(ans1 , ans2);
    tree[step].ans += tree[tree[step].lson].ans + tree[tree[step].rson].ans;
}
int main() {
    cin>>n;
    root = build();
    cnt = 0;
    dfs(root);
    printf("%lld\n",tree[root].ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值