[BZOJ2212][Poi2011]Tree Rotations(线段树合并)

Address

洛谷 P3521
BZOJ 2212
LOJ #2163

Solution

  • 非常有意思的题
  • 一个直观的想法
  • 对于一个点 u u
  • 计算出 c n t 1 cnt_1 表示叶子 v v u u 的左子树内, w w u u 的右子树内,满足 v v 的权值大于 w w 的权值的二元组 ( v , w ) (v,w) 个数
  • c n t 2 cnt_2 表示 v v 在右子树内, w w 在左子树内
  • 如果 c n t 1 > c n t 2 cnt_1>cnt_2 则交换 u u 的左右子树
  • 为最终答案贡献 min ( c n t 1 , c n t 2 ) \min(cnt_1,cnt_2)
  • 但事实上 c n t 1 + c n t 2 cnt_1+cnt_2 等于 u u 的左子树大小与右子树大小之积
  • 所以我们的关键点就是求 c n t 1 cnt_1
  • 考虑对每个节点开一棵动态开点权值线段树,储存子树内权值信息
  • u u 的左子节点为 l c lc ,右子节点为 r c rc
  • 合并 l c lc r c rc 所对应的权值线段树作为 u u 所对应的权值线段树
  • 在线段树合并的过程中统计 c n t 1 cnt_1
  • 具体地,如果合并线段树节点 x x y y 的子树
  • 那么为 c n t 1 cnt_1 贡献 s i z e [ r c x ] × s i z e [ l c y ] size[rc_x]\times size[lc_y]
  • 其中 l c x lc_x r c x rc_x 分别表示线段树节点 x x 的左右子节点
  • s i z e [ x ] size[x] 表示线段树节点 x x 对应值域的权值个数
  • 对于每个叶子节点,我们都会新建一条从线段树根到叶子的长度为 O ( log n ) O(\log n) 的链
  • 而两个线段树节点合并必然导致其中一个点被扔掉
  • 复杂度 O ( n log n ) O(n\log n)

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

template <class T>
T Min(T a, T b) {return a < b ? a : b;}

typedef long long ll;

const int N = 3e6 + 5;

int n, ToT, top, stk[N], tot;
ll ans, delta;

struct node
{
	int lc, rc, sum;
	
	void init()
	{
		lc = rc = sum = 0;
	}
} T[N];

struct aruba
{
	int x, y, z;
};

int newnode()
{
	if (top) return T[stk[top]].init(), stk[top--];
	return ++ToT;
}

int segtree(int l, int r, int pos)
{
	int u = newnode(), mid = l + r >> 1;
	T[u].sum = 1;
	if (l == r) return u;
	if (pos <= mid) T[u].lc = segtree(l, mid, pos);
	else T[u].rc = segtree(mid + 1, r, pos);
	return u;
}

int mer(int x, int y)
{
	if (!x || !y) return x + y;
	stk[++top] = y;
	T[x].sum += T[y].sum;
	delta += 1ll * T[T[x].rc].sum * T[T[y].lc].sum;
	T[x].lc = mer(T[x].lc, T[y].lc);
	T[x].rc = mer(T[x].rc, T[y].rc);
	return x;
}

aruba jiejuediao()
{
	int x = ++tot, p = read();
	if (p) return (aruba) {x, segtree(1, n, p), 1};
	aruba lc = jiejuediao(), rc = jiejuediao();
	delta = 0;
	int rt = mer(lc.y, rc.y);
	ans += Min(1ll * lc.z * rc.z - delta, delta);
	return (aruba) {x, rt, lc.z + rc.z};
}

int main()
{
	n = read();
	jiejuediao();
	std::cout << ans << std::endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/83902459