• 80: bzoj3705 线段树合并


    $des$

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

    $sol$

    可以发现每次交换对子树内的逆序对数没有影响。所以我们可以使每棵子树都最优。

    对每个叶子节点维护一棵权值线段树,自底向上更新.

     更新的时候枚举是否需要交换,然后把两棵线段树合并即可.

    注意动态开点.

    #include<bits/stdc++.h>
    
    #define ll long long
    
    using namespace std;
    
    #define Rep(i, a, b) for(int i = a; i <= b; i ++)
    
    
    #define gc getchar()
    inline int read() {
        int x = 0, f = 1;
        char c = gc;
        while(c < '0' || c > '9') {
            if(c == '-') f = -1;
            c = gc;
        }
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc;
        return x * f;
    }
    
    int n, sz, seg;
    ll ans, cnt1, cnt2;
    int v[400005], l[400005], r[400005], root[400005];
    int sum[4000005], ls[4000005], rs[4000005];
    
    namespace $ {
        void readtree(int x) {
            v[x] = read();
            if(!v[x]) {
                l[x] = ++ sz;
                readtree(l[x]);
                r[x] = ++ sz;
                readtree(r[x]);
            }
        }
    
        void pushup(int k) {
            sum[k] = sum[ls[k]] + sum[rs[k]];
        }
    
        void build(int &k, int l, int r, int val) {
            if(!k) k = ++ seg;
            if(l == r) {
                sum[k] = 1;
                return;
            }
            int mid = (l + r) >> 1;
            if(val <= mid) build(ls[k], l, mid, val);
            else build(rs[k], mid + 1, r, val);
            pushup(k);
        }
    
        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() {
        n = read();
        ++ sz;
        $:: readtree(1);
        Rep(i, 1, sz)
        if(v[i]) $:: build(root[i], 1, n, v[i]);
        $:: solve(1);
        cout << ans;
        return 0;
    }
  • 相关阅读:
    《架构之美》读后感(二)
    《架构之美》读后感(一)
    《代码大全2》阅读笔记03
    《代码大全2》阅读笔记02
    《代码大全2》阅读笔记01
    学习进度报告(十四)
    软件方法阅读笔记03
    第二讲
    1.26 十讲第一讲
    1.23
  • 原文地址:https://www.cnblogs.com/shandongs1/p/9863565.html
Copyright © 2020-2023  润新知