码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-02-11 00:08:18      阅读:161      评论:0      收藏:0      [点我收藏+]

标签:计算   sort   情况下   编号   clu   直接   合并   rip   ons   

Description

给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点。每个点有一个权值v_i。
你需要将这棵树转化成一个大根堆。确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j。
请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树。

Solution

思路比较直接
\(f[i][j]\) 表示\(i\)为子树的节点中,堆中最大值小于\(j\)的情况下能选的最多点数
转移时就是用一个前缀最大值更新一个后缀
用线段树维护即可,向上推时顺便把线段树合并
区间max直接打一个永久化标记
还要打区间加法标记,下放即可
注意各种地方都要下放,QwQ

#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int n,head[N],nxt[N<<1],to[N<<1],num=0,m,a[N],b[N],cnt=0,rt[N],ans=0;
inline void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
struct node{
  int ls,rs,la,w;
}tr[N*24];
inline void pushdown(int x){
  int ls=tr[x].ls,rs=tr[x].rs;
  if(ls)tr[ls].la+=tr[x].la,tr[ls].w=max(tr[x].w,tr[x].la+tr[ls].w);
  if(rs)tr[rs].la+=tr[x].la,tr[rs].w=max(tr[x].w,tr[x].la+tr[rs].w);
  tr[x].la=0;
}
inline void add(int &x,int l,int r,int sa,int se,int t){
  if(!x)x=++cnt;
  if(sa<=l && r<=se){tr[x].w=max(tr[x].w,t);return ;}
  pushdown(x);
  int mid=(l+r)>>1;
  if(se<=mid)add(tr[x].ls,l,mid,sa,se,t);
  else if(sa>mid)add(tr[x].rs,mid+1,r,sa,se,t);
  else add(tr[x].ls,l,mid,sa,mid,t),add(tr[x].rs,mid+1,r,mid+1,se,t);
}
inline int merge(int x,int y){
  if(!x||!y)return x+y;
  pushdown(x);pushdown(y);
  if(!tr[x].ls)
    tr[x].ls=tr[y].ls,tr[tr[x].ls].w+=tr[x].w,tr[tr[x].ls].la+=tr[x].w+tr[x].la;
  else if(!tr[y].ls)tr[tr[x].ls].w+=tr[y].w,tr[tr[x].ls].la+=tr[y].w+tr[y].la;
  else tr[x].ls=merge(tr[x].ls,tr[y].ls);

  if(!tr[x].rs)
    tr[x].rs=tr[y].rs,tr[tr[x].rs].w+=tr[x].w,tr[tr[x].rs].la+=tr[x].w+tr[x].la;
  else if(!tr[y].rs)tr[tr[x].rs].w+=tr[y].w,tr[tr[x].rs].la+=tr[y].w+tr[y].la;
  else tr[x].rs=merge(tr[x].rs,tr[y].rs);
  tr[x].w+=tr[y].w;
  return x;
}
inline int qry(int x,int l,int r,int sa){
  if(!x || !sa)return 0;
  if(l==r)return tr[x].w;
  int mid=(l+r)>>1;
  pushdown(x);
  if(sa<=mid)return max(qry(tr[x].ls,l,mid,sa),tr[x].w);
  return max(qry(tr[x].rs,mid+1,r,sa),tr[x].w);
}
inline void dfs(int x){
  for(int i=head[x];i;i=nxt[i])dfs(to[i]),rt[x]=merge(rt[x],rt[to[i]]);
  add(rt[x],1,m,a[x],m,qry(rt[x],1,m,a[x]-1)+1);
}
inline void DFS(int x){
  if(!x)return ;
  ans=max(tr[x].w,ans);
  pushdown(x);
  DFS(tr[x].ls);DFS(tr[x].rs);
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  scanf("%d",&n);
  for(int i=1,x;i<=n;i++){
    scanf("%d%d",&a[i],&x);
    if(x)link(x,i);b[i]=a[i];
  }
  sort(b+1,b+n+1);m=unique(b+1,b+n+1)-b-1;
  for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+m+1,a[i])-b;
  dfs(1);DFS(rt[1]);
  cout<<ans<<endl;
  return 0;
}

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

标签:计算   sort   情况下   编号   clu   直接   合并   rip   ons   

原文地址:https://www.cnblogs.com/Yuzao/p/8439996.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!