「POI2011 R2 Day2」Tree Rotations【线段树合并】

题目链接

【BZOJ】
【洛谷】
【LOJ】

题解

由于是前序遍历,那么讨论一棵树上的逆序对的情况。

  • 两个节点都在左子树上
  • 两个节点都在右子树上
  • 两个节点分别在不同的子树上。

前两种情况其实也可以归结于第三种情况。

原因

因为两个节点不可能占据一个位置。
根据容斥原理,为了保证答案的正确性,我们递归求解不能计算两遍相同的答案。

回到正题
所以我们只需要讨论跨越两个子树的情况。
很显然,左子树中的所有点的 d f s dfs dfs序都比右子树的子树中的小。
那么如果要交换,就是相反一下。
比较容易可以想到对于每一个节点都先建立一个权值线段树。
那么每一次的答案就从线段树的左子树和右子树中取最小值就可以了。

代码

#include <bits/stdc++.h>
using namespace std;
namespace IOstream {
	#define gc getchar
	template <typename T> 
	inline void read(T &x) {
		x = 0; T fl = 1; char c = 0; 
		for (; c < '0' || c > '9'; c = gc()) 
			if (c == '-') fl = -1;
		for (; c >= '0' && c <= '9'; c = gc()) 
			x = (x << 1) + (x << 3) + (c ^ 48);
		x *= fl;
	}
	#undef gc
} using namespace IOstream;
typedef long long ll;
const int N = 4000005 + 6;
int n, cnt = 0, tot = 0; 
int rt[N]; 
ll ans, f, g;
struct node {
	int lc, rc, sz;
} tr[N];
int merge(int x, int y) {
	if (!x || !y) return x + y;
	f += 1ll * tr[tr[x].lc].sz * 1ll * tr[tr[y].rc].sz;
	g += 1ll * tr[tr[y].lc].sz * 1ll * tr[tr[x].rc].sz; 
	tr[x].lc = merge(tr[x].lc, tr[y].lc);
	tr[x].rc = merge(tr[x].rc, tr[y].rc);
	tr[x].sz += tr[y].sz; 
	return x; 
}
void ins(int nod, int l, int r, int k) {
	tr[nod].sz ++; 
	if (l == r) return;
	int mid = (l + r) >> 1;
	if (k <= mid) tr[nod].lc = ++ tot, ins(tr[nod].lc, l, mid, k);
	else tr[nod].rc = ++ tot, ins(tr[nod].rc, mid + 1, r, k); 
}
#undef ls
#undef rs 
int dfs() {
	int w; read(w);
	if (w) {
		rt[++ cnt] = ++ tot; 
		ins(rt[cnt], 1, n, w);
		return rt[cnt]; 
	} else {
		int nod = merge(dfs(), dfs());
		ans += min(f, g);
		f = g = 0; 
		return nod; 
	}
}
int main() {
	read(n); ans = 0ll; 
	dfs();
	printf("%lld\n", ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值