HZOI 大根堆 线段树合并
Posted sonnety-v0cali0d-kksk
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HZOI 大根堆 线段树合并相关的知识,希望对你有一定的参考价值。
题目描述
给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点。每个点有一个权值v_i。
你需要将这棵树转化成一个大根堆。确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j。
请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树。
输入格式
第一行包含一个正整数n(1<=n<=200000),表示节点的个数。
接下来n行,每行两个整数v_i,p_i(0<=v_i<=10^9,1<=p_i<i,p_1=0),表示每个节点的权值与父亲。
输出格式
输出一行一个正整数,即最多的点数。
样例
样例输入
6
3 0
1 1
2 1
3 1
4 1
5 1
样例输出
5
私货:
摆了,有时间在详细写。
思路历程:
1.最开始想最暴力的方法,我先把树建出来,然后对每一个点固定为树根,我暴力搜它的子树,pushup再pushup,存进去我的数组,赢!
今天你赢赢赢,明天我输光光
但是专题的名字叫线段树合并啊
2.或许dp可以解决这个问题,我还是固定一个点作为树根,但是我用\\(f_u,_i\\)表示以u为树根的子树里的节点权值小于i的个数。\\(i\\)<=\\(v_u\\).
我们用\\(size_u\\)表示u的子节点大小。
就有了转移方程:
………………
………………
推出来了吗?
我没推出来呢()
欸要不你悄悄把转移方程洛谷V我,要不然这个博客就结束了()
…………
ok现在我们得到了转移方程,(感\\(K_8He\\)老师的博客对这个fw的大力支持)
\\(1....\\)\\(f_u,_i=\\sum\\limits_size_u^\\) \\(f_u,_i\\) , \\(i<=v_u\\)
\\(2....\\)\\(f_u,_i=max(\\sum\\limits_size_u^f_u,_i,\\sum\\limits_size_u^\\) \\(f_u,_i+1\\)-1) , \\(i>=v_u\\)
贴贴(链接)
ps:没有贴错人也没有贴错题,详见下()
再贴
ok现在不会了去看题解了拜拜
先这样吧一会在写代码,我转移方程应该是错的()回来再改
BZOJ4919[Lydsy1706月赛]大根堆-------------线段树进阶
是不是每做道线段树进阶都要写个题解。。根本不会写
Description
给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点。每个点有一个权值v_i。 你需要将这棵树转化成一个大根堆。确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j。 请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树。
Solution
看起来很像一道DP,实际上也确实要用到DP的思想。
设编号为i的点的子树中最大权值为j的最优方案为fi,j,i的权值为vi
在线段树中维护f,进行标记永久化,不需要进行pushup,在求和时累加路径上的变化量即可。
那么我们就能在对树DFS的过程中进行对f的更新。在每次搜到叶节点后开线段树,回溯时进行线段树合并,并进行对f的维护。
对fi的转移,有取i点和不取i点两种情况。不取i时f最小值为子树中最大权值为v[i]的方案总和,取i时为子树中最大权值为v[i]-1的方案总和数+1。
若不取i的最小值大于取i最大值,则直接return
反之则利用f与v的单调正相关的关系进行二分,找到最优决策点并在线段树中更新即可。
具体看代码
Code:
1 #include<bits/stdc++.h> 2 #define debug cout<<"wrong"<<endl 3 using namespace std; 4 const int NN=2e5+10; 5 int rt[NN],to[NN],nex[NN],head[NN],v[NN],num,ext,n; 6 inline int read(){ 7 int x=0,f=1; 8 char ch=getchar(); 9 while(ch<\'0\'||ch>\'9\'){ 10 if(ch==\'-\') f=-1; 11 ch=getchar(); 12 } 13 while(ch<=\'9\'&&ch>=\'0\'){ 14 x=(x<<1)+(x<<3)+(ch^48); 15 ch=getchar(); 16 } 17 return x*f; 18 } 19 inline void add(int a,int b){ 20 to[++num]=b; nex[num]=head[a]; head[a]=num; 21 } 22 void write(int x){ 23 if(x<0) putchar(\'-\'), x=-x; 24 if(x>9) write(x/10); 25 putchar(x%10+\'0\'); 26 } 27 void init(){ 28 n=read(); 29 for(register int i=1;i<=n;i++){ 30 v[i]=read(); 31 add(read(),i); 32 } 33 int d[NN]; 34 for(register int i=1;i<=num;i++) d[i]=v[i]; 35 sort(d+1,d+1+num); 36 ext=unique(d+1,d+1+num)-d-1; 37 for(register int i=1;i<=num;i++) v[i]=lower_bound(d+1,d+ext+1,v[i])-d; 38 } 39 struct node{ 40 int seg,ls[NN*40],rs[NN*40],num[NN*40]; 41 void insert(int &x,int l,int r,int opl,int opr,int val){ 42 if(!x) x=++seg; 43 if(opl<=l&&opr>=r){ num[x]+=val; return; } 44 int mid=(l+r)>>1; 45 if(opl<=mid) insert(ls[x],l,mid,opl,opr,val); 46 if(opr>mid) insert(rs[x],mid+1,r,opl,opr,val); 47 } 48 void marge(int &x,int y,int l,int r){ 49 if(!x||!y){ x=x+y; return; } 50 num[x]+=num[y]; 51 int mid=(l+r)>>1; 52 marge(ls[x],ls[y],l,mid); 53 marge(rs[x],rs[y],mid+1,r); 54 } 55 int query(int x,int l,int r,int pos){ 56 if(!x) return 0; 57 int mid=(l+r)>>1,p=num[x]; 58 if(pos<=mid) return query(ls[x],l,mid,pos)+p; 59 else return query(rs[x],mid+1,r,pos)+p; 60 } 61 }segt; 62 void dfs(int x){ 63 for(register int i=head[x];i;i=nex[i]){ 64 dfs(to[i]); 65 segt.marge(rt[x],rt[to[i]],1,ext); 66 } 67 int ans1=segt.query(rt[x],1,ext,v[x]-1)+1; 68 int ans2=segt.query(rt[x],1,ext,v[x]); 69 if(ans1<=ans2) return; 70 int l=v[x],r=ext,mid,pos=v[x]; 71 while(l<=r){ 72 mid=(l+r)>>1; 73 if(segt.query(rt[x],1,ext,mid)<ans1) l=mid+1, pos=mid; 74 else r=mid-1; 75 } 76 segt.insert(rt[x],1,ext,v[x],pos,1); 77 } 78 int main(){ 79 init(); 80 dfs(1); 81 write(segt.query(rt[1],1,ext,ext)); 82 putchar(\'\\n\'); 83 return 0; 84 }
以上是关于HZOI 大根堆 线段树合并的主要内容,如果未能解决你的问题,请参考以下文章