洛谷 P3521 [POI2011]ROT-Tree Rotations

Description

洛谷传送门

Solution

线段树合并

显然,两棵树的交换与他们的子树无关,所以从下往上处理即可。

我们考虑对于每个子节点建一只权值线段树。然后不断向上合并。

注意:每个点都是一只完整的权值线段树,也就是说左子树权值小于右子树权值。

所以逆序对个数就很明显了。

  • 不交换: \(s1 += t[rs(u)].sum * t[ls(v)].sum\)

  • 交换: \(s2 += t[ls(u)].sum * t[rs(v)].sum\)

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ls(x) t[x].l
#define rs(x) t[x].r
#define ll long long

using namespace std;

const int N = 4e6 + 10;
int n, cnt;
ll s1, s2, ans;
struct Seg_tree{
	int l, r, sum;
}t[N];

inline void update(int &x, int l, int r, int pos){
	if(!x) x = ++cnt;
	t[x].sum++;
	if(l == r) return;
	int mid = (l + r) >> 1;
	if(pos <= mid) update(ls(x), l, mid, pos);
	else update(rs(x), mid + 1, r, pos);
}

inline int merge(int u, int v, int l, int r){
	if(!u || !v) return u | v;
	t[u].sum += t[v].sum;
	s1 += 1ll * t[rs(u)].sum * t[ls(v)].sum;
	s2 += 1ll * t[ls(u)].sum * t[rs(v)].sum;
	int mid = (l + r) >> 1;
	ls(u) = merge(ls(u), ls(v), l, mid);
	rs(u) = merge(rs(u), rs(v), mid + 1, r);
	return u;
}

inline void dfs(int &x){
	int t;
	scanf("%d", &t);
	if(!t){
		int u = 0, v = 0;
		dfs(u), dfs(v);
		s1 = 0, s2 = 0;
		x = u;
		x = merge(x, v, 1, n);
		ans += min(s1, s2);
	}else update(x, 1, n, t);
}

int main(){
	scanf("%d", &n);
	int root = 0;
	dfs(root);
	printf("%lld\n", ans);
	return 0;
}

End

posted @ 2021-10-05 21:53  xixike  阅读(36)  评论(0编辑  收藏  举报