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