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;
}