BZOJ4919[Lydsy1706月赛]大根堆-------------线段树进阶

Posted keen_z

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

以上是关于BZOJ4919[Lydsy1706月赛]大根堆-------------线段树进阶的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4919[Lydsy六月月赛]大根堆 线段树合并

bzoj 4919: [Lydsy六月月赛]大根堆

BZOJ4919[Lydsy六月月赛]大根堆

bzoj4919: 大根堆

[BZOJ4919]大根堆

[bzoj4919]大根堆