[BZOJ 2212] [POI 2011] Tree Rotations
阿新 • • 發佈:2018-11-27
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; }