BZOJ4919:[Lydsy1706月赛]大根堆(set启发式合并)

Description

给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点。每个点有一个权值v_i。
你需要将这棵树转化成一个大根堆。确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j。
请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树。

Input

第一行包含一个正整数n(1<=n<=200000),表示节点的个数。
接下来n行,每行两个整数v_i,p_i(0<=v_i<=10^9,1<=p_i<i,p_1=0),表示每个节点的权值与父亲。

Output

输出一行一个正整数,即最多的点数。

Sample Input

6
3 0
1 1
2 1
3 1
4 1
5 1

Sample Output

5

Solution

挺巧妙的……

考虑如果只是在序列上做的话,其实这个就是个$LIS$。

现在把他搬到树上其实也差不多,可以每个点开个$multiset$,也就是$nlogn$的$LIS$中的那个单调栈。

每个节点把儿子启发式合并,然后像序列$LIS$一样找到第一个大于等于它的这个数删掉并把它加入。

答案就是根节点$multiset$的$size$

Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<set>
 4 #define N (200009)
 5 using namespace std;
 6 
 7 struct Edge{int to,next;}edge[N];
 8 int n,x,v[N];
 9 int head[N],num_edge;
10 multiset<int>S[N];
11 multiset<int>::iterator it;
12 
13 void add(int u,int v)
14 {
15     edge[++num_edge].to=v;
16     edge[num_edge].next=head[u];
17     head[u]=num_edge;
18 }
19 
20 void DFS(int x)
21 {
22     for (int i=head[x]; i; i=edge[i].next)
23     {
24         int y=edge[i].to; DFS(y);
25         if (S[x].size()<S[y].size()) swap(S[x],S[y]);
26         for (it=S[y].begin(); it!=S[y].end(); ++it) S[x].insert(*it);
27         S[y].clear();
28     }
29     it=S[x].lower_bound(v[x]);
30     if (it!=S[x].end()) S[x].erase(it);
31     S[x].insert(v[x]);
32 }
33 
34 int main()
35 {
36     scanf("%d",&n);
37     for (int i=1; i<=n; ++i)
38     {
39         scanf("%d%d",&v[i],&x);
40         if (x) add(x,i);
41     }
42     DFS(1);
43     printf("%d
",S[1].size());
44 }
原文地址:https://www.cnblogs.com/refun/p/10224652.html