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

 #include<iostream>
#include<cstdio>
#include<set>
#define N (200009)
using namespace std; struct Edge{int to,next;}edge[N];
int n,x,v[N];
int head[N],num_edge;
multiset<int>S[N];
multiset<int>::iterator it; void add(int u,int v)
{
edge[++num_edge].to=v;
edge[num_edge].next=head[u];
head[u]=num_edge;
} void DFS(int x)
{
for (int i=head[x]; i; i=edge[i].next)
{
int y=edge[i].to; DFS(y);
if (S[x].size()<S[y].size()) swap(S[x],S[y]);
for (it=S[y].begin(); it!=S[y].end(); ++it) S[x].insert(*it);
S[y].clear();
}
it=S[x].lower_bound(v[x]);
if (it!=S[x].end()) S[x].erase(it);
S[x].insert(v[x]);
} int main()
{
scanf("%d",&n);
for (int i=; i<=n; ++i)
{
scanf("%d%d",&v[i],&x);
if (x) add(x,i);
}
DFS();
printf("%d\n",S[].size());
}
上一篇:转UI么?不想


下一篇:activiti 临时笔记mark