1. 程式人生 > >[BZOJ2212][Poi2011]Tree Rotations(線段樹合併)

[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;
}