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;
}
posted @ 2020-02-16 16:19  Mrzdtz220  阅读(83)  评论(0编辑  收藏  举报