bzoj4919 [Lydsy1706月赛]大根堆
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
3 0
1 1
2 1
3 1
4 1
5 1
Sample Output
5
(不)容易看出这是一个变种LIS。
如果只有一条链就是LIS。
如果多条链但是最终选的点是一条链也是个树版LIS。
然后感受一下这是个LIS。
LIS有一个经典二分做法。
现在我们用set来保存二分做法的那个数组。
不同子树之间用set来启发式合并。
然后把这个点权值丢进去替换掉第一个>=它的。
最后输出set的大小。
一棵树,每个点u有权值val[u],
要求选出最多的点,并且满足每个被选的点子树中没有权值大于等于该点权值。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define rep(i,a,b) for(R i=a;i<=b;i++) 5 #define Rep(i,a,b) for(R i=a;i>=b;i--) 6 #define rp(i,x) for(R i=H[x];i!=-1;i=E[i].nt) 7 #define ms(i,a) memset(a,i,sizeof(a)) 8 #define gc() getchar() 9 template<class T>void read(T &x){ 10 x=0; char c=0; 11 while (!isdigit(c)) c=gc(); 12 while (isdigit(c)) x=x*10+(c^48),c=gc(); 13 } 14 int const maxn=200000+3; 15 multiset<int> s[maxn]; 16 int a[maxn],H[maxn],cnt,n; 17 struct Edge{ 18 int to,nt; 19 }E[maxn]; 20 void add(int a,int b){ 21 E[cnt]=(Edge){b,H[a]};H[a]=cnt++; 22 } 23 void merge(int x,int y){ 24 if(s[x].size()>s[y].size()) swap(s[x],s[y]); 25 multiset<int> :: iterator it=s[x].begin(); 26 for( ; it!=s[x].end(); it++) s[y].insert(*it) ; 27 s[x].clear(); 28 } 29 void dfs(int x){ 30 rp(i,x){ 31 int v=E[i].to; 32 dfs(v); merge(v,x); 33 } 34 multiset<int> :: iterator it=s[x].lower_bound(a[x]); 35 if(it!=s[x].end()) s[x].erase(it); 36 s[x].insert(a[x]); 37 } 38 int main(){ 39 read(n); 40 ms(-1,H); 41 rep(i,1,n){ 42 read(a[i]); 43 int k; read(k); 44 if(k) add(k,i); 45 } 46 dfs(1); 47 printf("%d\n",s[1].size()); 48 return 0; 49 }