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 }
Code

 

以上是关于HZOI 大根堆 线段树合并的主要内容,如果未能解决你的问题,请参考以下文章

线段树 大根堆 模版二合一

bzoj 4504: K个串大根堆+主席树

BZOJ4919[Lydsy六月月赛]大根堆

启发式合并(堆setsplaytreap)/线段树合并学习小记

BZOJ3252: 攻略 可并堆

四边形不等式COGS1658- [HZOI 2014] 合并石子