BZOJ 2212: [Poi2011]Tree Rotations
计算序列的逆序对,交换相邻两个位置只会影响它俩是否产生逆序对,和其他位置的大小关系无关
交换相邻两段同样只影响这两段之间形成的逆序对
那么对每一个节点开一棵权值线段树,维护它的子树内有哪些权值
然后线段树合并,将两个儿子的线段树合并到父亲的身上
合并左右的时候就考虑会怎样产生逆序对
假设左儿子为 \(u\),右儿子为 \(v\),合并过程的区间为 \([l,r]\)
若不交换,就是 \(u\) 的 \([mid+1, r]\) 和 \(v\) 的 \([l, mid]\) 产生逆序对
若交换,就是 \(v\) 的 \([mid + 1,r]\) 和 \(u\) 的 \([l, mid]\) 产生逆序对
然后答案加上其中较小值即可
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
return x * f;
}
const int N = 5e6 + 10;
struct Tree { ll v; int ls, rs; } tree[N];
int n, tol, cnt = 1, num[N], ls[N], rs[N], rts[N];
ll ans, ans1, ans2;
void in(int p) {
num[p] = read();
if (!num[p]) {
in(ls[p] = ++cnt);
in(rs[p] = ++cnt);
}
}
inline void pushup(int p) {
tree[p].v = tree[tree[p].ls].v + tree[tree[p].rs].v;
}
void update(int &p, int l, int r, int v) {
if (!p) p = ++tol;
if (l == r) {
tree[p].v++;
return;
}
int mid = l + r >> 1;
if (v <= mid) update(tree[p].ls, l, mid, v);
else update(tree[p].rs, mid + 1, r, v);
pushup(p);
}
int merge(int u, int v) {
if (!u || !v) return u + v;
ans1 += 1LL * tree[tree[u].rs].v * 1LL * tree[tree[v].ls].v;
ans2 += 1LL * tree[tree[v].rs].v * 1LL * tree[tree[u].ls].v;
tree[u].ls = merge(tree[u].ls, tree[v].ls);
tree[u].rs = merge(tree[u].rs, tree[v].rs);
pushup(u);
return u;
}
void dfs(int u) {
if (!u) return;
dfs(ls[u]);
dfs(rs[u]);
if (!num[u]) {
ans1 = ans2 = 0;
rts[u] = merge(rts[ls[u]], rts[rs[u]]);
ans += min(ans1, ans2);
}
}
int main() {
n = read();
in(1);
for (int i = 1; i <= cnt; i++) {
if (num[i])
update(rts[i], 1, n, num[i]);
}
dfs(1);
printf("%lld\n", ans);
return 0;
}