[BZOJ 2212] [POI 2011] Tree Rotations

Description

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

Solution

建立权值线段树,线段树合并的时候顺便求出逆序对个数。

Code

#include <cstdio>

const int N = 400005, M = 8000005;
int n, cnt1, cnt2, a[N], ch[N][2], root[N], sum[M], lson[M], rson[M];
long long ans, res1, res2;

int read() {
    int x = 0; char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while (c >= '0' && c <= '9') {
        x = (x << 3) + (x << 1) + (c ^ 48);
        c = getchar();
    }
    return x;
}
void dfs(int x) {
    a[x] = read();
    if (!a[x]) {
        ch[x][0] = ++cnt1, dfs(ch[x][0]);
        ch[x][1] = ++cnt1, dfs(ch[x][1]);
    }
}
void build(int &cur, int l, int r, int x) {
    if (!cur) cur = ++cnt2;
    ++sum[cur];
    if (l == r) return;
    int mid = l + ((r - l) >> 1);
    if (x <= mid) build(lson[cur], l, mid, x);
    else build(rson[cur], mid + 1, r, x);
}
int merge(int x, int y) {
    if (!x) return y;
    if (!y) return x;
    res1 += 1LL * sum[rson[x]] * sum[lson[y]];
    res2 += 1LL * sum[lson[x]] * sum[rson[y]];
    lson[x] = merge(lson[x], lson[y]);
    rson[x] = merge(rson[x], rson[y]);
    sum[x] = sum[lson[x]] + sum[rson[x]];
    return x;
}
void solve(int x) {
    if (!x) return;
    solve(ch[x][0]), solve(ch[x][1]);
    if (!a[x]) {
        res1 = res2 = 0;
        root[x] = merge(root[ch[x][0]], root[ch[x][1]]);
        ans += res1 < res2 ? res1 : res2;
    }
}

int main() {
    n = read();
    dfs(++cnt1);
    for (int i = 1; i <= cnt1; ++i)
        if (a[i]) build(root[i], 1, n, a[i]);
    solve(1);
    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/fly-in-milkyway/p/10024270.html