[POI2011] ROT-Tree Rotations 题解 && 权值线段树模板

标签 : 权值线段树


权值线段树,顾名思义,线段树维护一段值域的点数或其他信息。

若点 \(p\) 的值域为 \([l, r]\), 则 \(lson\)\(rson\) 的值域分别为 \([l, mid], (mid, r]\), 依此类推。

因此,容易发现,权值线段树通常搭配动态开点线段树使用。


结论:对于每个节点,其内部顺序对于外部答案无影响。

简单考虑内外关系即可。

在此题中,我们需要记录每个叶子节点的权值线段树,并向上合并,在合并过程中求得逆序对个数。

我们将每个节点的左右儿子的左右儿子分别考虑,分类讨论左子树在前与右子树在前的逆序对贡献。

注意,我们在合并的过程中有层数区别,故我们在当前这一层只考虑当前这一层的贡献。

说简单点,就是

  • 左在前 :ans1 += t[t[lson].rson].siz * t[t[rson].lson].siz;
  • 右在前 :ans2 += t[t[lson].lson].siz * t[t[rson].rson].siz;

然后合并左儿子 :t[x].lson = merge(t[lson].lson, t[rson].lson]);
然后合并右儿子 :t[x].rson = merge(t[lson].rson, t[rson].rson]);

儿子的贡献会在合并儿子时计算。


#include <bits/stdc++.h>
//#define int long long 
using namespace std;
int read() {
	int f = 1, x = 0; char c = getchar();
	while(!isdigit(c)) {if(c == '-') f = -f; c = getchar();}
	while(isdigit(c)) {x = (x << 1) + (x << 3) + c - '0'; c = getchar();}
	return x * f;
}
const int N = 2e5 + 5;
long long ans, ans1, ans2;
int n, m, p, q, now, dfn;
struct node {
	int sum, lson, rson;
} t[N * 20];
void update(int &x, int l, int r, int p) {
	if(!x) x = ++ now;
	t[x].sum ++; 
	if(l == r) return;
	int mid = l + r >> 1;
	if(p <= mid) update(t[x].lson, l, mid, p);
	else update(t[x].rson, mid + 1, r, p);
}
int merge(int x, int y) {
	if(!x || !y) return x + y;
	t[x].sum += t[y].sum;
	ans1 += 1ll * t[t[x].lson].sum * t[t[y].rson].sum;
	ans2 += 1ll * t[t[x].rson].sum * t[t[y].lson].sum;
	t[x].lson = merge(t[x].lson, t[y].lson);
	t[x].rson = merge(t[x].rson, t[y].rson);
	return x;
}
void readin(int &x) {
	int lson, rson;
	x = 0;
	int d = read();
	if(!d) {
		readin(lson), readin(rson);
		ans1 = 0ll, ans2 = 0ll;
		x = merge(lson, rson);
		ans += min(ans1, ans2);
//		return ;
	} else {
		update(x, 1, n, d);
	}
//	cerr << x << "\n";
}
int root;
signed main() { 
	n = read();
	readin(root);	
	cout << ans;
	cerr << 1.0 * CLOCKS_PER_SEC << '\n';
	return 0;
}
posted @ 2021-10-03 17:30  永远_少年  阅读(45)  评论(0编辑  收藏  举报