• 「LOJ #2163」「POI2011」Tree Rotations


    Description

    现在有一棵二叉树,所有非叶子节点都有两个孩子。在每个叶子节点上有一个权值(有 (n) 个叶子节点,满足这些权值为 (1cdots n) 的一个排列)。

    可以任意交换每个非叶子节点的左右孩子。

    要求进行一系列交换,使得最终所有叶子节点的权值按照遍历序写出来,逆序对个数最少。

    Hint

    (1le nle 2 imes 10^5)

    Solution

    可以发现,对于其中一颗子树,不论如何变换都不会改变 这一颗子树中的结点与其他位置的结点组合产生的逆序对的个数 即这颗子树的变换不会改变上层之间的逆序对,所以只要考虑这颗子树中的就行了。

    首先对所有结点开一个权值线段树,之后逐层合并即可。现在的问题就是,左右子树如何放置(是否交换)产生的逆序对最小。

    我们可以在线段树合并的时候干这个事情。具体地,就是说当合并到结点 (a) (左子树结点) (, b)(右子树结点) 时,记 不交换产生的逆序对为 (u) ,交换的为 (v),那么:

    • (u) 加上 (a) 的右子树的大小 ( imes b) 左子树的大小
    • (v) 加上 (a) 的左子树的大小 ( imes b) 右子树的大小

    一次合并完之后,( ext{ans} leftarrow ext{ans} + min(u, v)) 即可。时空复杂度 (O(nlog n))

    注意,实际节点数可能有 (n imes 2) 这么多,应开大空间。

    Code

    /*
     * Author : _Wallace_
     * Source : https://www.cnblogs.com/-Wallace-/
     * Problem : LOJ #2163 POI2011 Tree Rotations
     */
    #include <iostream>
    using namespace std;
    
    const int N = 4e5 + 5;
    const int S = N << 6;
    
    int lc[S], rc[S], total = 0;
    long long sum[S];
    #define mid ((l + r) >> 1)
    void increase(int &rt, int l, int r, int p) {
    	if (!rt) rt = ++total;
    	sum[rt]++;
    	if (l == r) return;
    	if (p <= mid) increase(lc[rt], l, mid, p);
    	else increase(rc[rt], mid + 1, r, p);
    }
    int merge(int a, int b, int l, int r, long long& u, long long& v) {
    	if (!a || !b) return a | b;
    	if (l == r) return sum[a] += sum[b], a;
    	u += sum[rc[a]] * sum[lc[b]];
    	v += sum[lc[a]] * sum[rc[b]];
    	lc[a] = merge(lc[a], lc[b], l, mid, u, v);
    	rc[a] = merge(rc[a], rc[b], mid + 1, r, u, v);
    	return sum[a] = sum[lc[a]] + sum[rc[a]], a;
    }
    #undef mid
    
    int n;
    long long ans = 0ll;
    int solve() {
    	int rt = 0, cur;
    	cin >> cur;
    	if (!cur) {
    		int l = solve(), r = solve();
    		long long u = 0ll, v = 0ll;
    		rt = merge(l, r, 1, n, u, v);
    		ans += min(u, v);
    	} else increase(rt, 1, n, cur);
    	return rt;
    }
    
    signed main() {
    	cin >> n;
    	solve();
    	cout << ans << endl;
    	return 0;
    }
    
  • 相关阅读:
    搭建第一个web项目:Struts+hibernate+spring配置(annotation)
    Visual Studio
    Javascript的性能瓶颈
    导出数据库文档的最简单的方式
    long类型在C#和C++中的异同
    GDI+创建Graphics对象的2种方式
    jQuery中click()与trigger方法的区别
    使用VS调试64位应用程序
    ASP.NET中多个相同name的控件在后台正确取值
    js中的eval方法转换对象时,为何一定要加上括号?
  • 原文地址:https://www.cnblogs.com/-Wallace-/p/12723272.html
Copyright © 2020-2023  润新知