「计蒜客模拟赛」抉择

「计蒜客模拟赛」抉择

Summarize

给定有根树,点有权值,要求选出一些点,满足集合中的点的祖先权值比它小,求集合大小的最大值。

$n\le 300000$

Solution

利用单调不下降子序列的 $O(n\log n)$ 解法,记 $d_i$ 为子树中集合大小为 $i$ 时,节点权值的最小值。向上利用启发式合并维护即可。

也可使用线段树优化树上 DP 实现。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 3e5 + 5;

int n;
int a[MAXN], id[MAXN];
vector<int> G[MAXN];
set<int> s[MAXN];
int ans = 0;

void dfs(int u, int fa) {
int lst = 0;
for (vector<int>::iterator it = G[u].begin(); it != G[u].end(); it++) {
int v = *it;
if (v == fa) continue;
dfs(v, u);
if (lst) {
if (s[id[lst]].size() > s[id[v]].size()) swap(id[lst], id[v]);
int t = s[id[lst]].size();
for (set<int>::iterator it = s[id[lst]].begin(); it != s[id[lst]].end(); it++)
s[id[v]].insert(*it);
}
lst = v;
}
if (lst) {
set<int>::iterator it = s[id[lst]].lower_bound(a[u]);
if (it == s[id[lst]].begin()) {
s[id[lst]].insert(a[u]);
} else {
it--;
s[id[lst]].erase(it);
s[id[lst]].insert(a[u]);
}
swap(id[lst], id[u]);
} else {
s[id[u]].insert(a[u]);
}
int t = s[id[u]].size();
ans = max(ans, (int)s[id[u]].size());
}


int main() {
freopen("choice.in", "r", stdin);
freopen("choice.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), id[i] = i;
for (int i = 1; i < n; ++i) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v), G[v].push_back(u);
}
dfs(1, 0);
printf("%d", ans);
return 0;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×