[BZOJ2212][Poi2011]Tree Rotations(線段樹合併)
阿新 • • 發佈:2018-11-23
Address
Solution
- 非常有意思的題
- 一個直觀的想法
- 對於一個點
- 計算出 表示葉子 在 的左子樹內, 在 的右子樹內,滿足 的權值大於 的權值的二元組 個數
- 表示 在右子樹內, 在左子樹內
- 如果 則交換 的左右子樹
- 為最終答案貢獻
- 但事實上 等於 的左子樹大小與右子樹大小之積
- 所以我們的關鍵點就是求
- 考慮對每個節點開一棵動態開點權值線段樹,儲存子樹內權值資訊
- 設 的左子節點為 ,右子節點為
- 合併 和 所對應的權值線段樹作為 所對應的權值線段樹
- 線上段樹合併的過程中統計
- 具體地,如果合併線段樹節點 和 的子樹
- 那麼為 貢獻
- 其中 和 分別表示線段樹節點 的左右子節點
- 表示線段樹節點 對應值域的權值個數
- 對於每個葉子節點,我們都會新建一條從線段樹根到葉子的長度為 的鏈
- 而兩個線段樹節點合併必然導致其中一個點被扔掉
- 複雜度
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;
}