[bzoj4919] [Lydsy1706月赛]大根堆

2 篇文章 0 订阅
1 篇文章 0 订阅

思路

将链上的最长上升子序列问题拓展到树上,dfs一遍,使用线段树合并+标记永久化维护dp数组。

代码

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
#define maxn 200020  
#define logn 19  
using namespace std;  
int n,a[maxn],cnt,tmp[maxn];  
inline void get(int &x){  
    char c=getchar();bool p=0;  
    while((c<'0'||c>'9')&&c!='-')c=getchar();  
    if(c=='-')p=1,c=getchar();x=c-'0',c=getchar();  
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();  
    if(p)x=-x;  
}  
int tot,la[maxn];  
struct edge{  
    int v,ne;  
}e[maxn];  
inline void add(int u,int v){e[tot].v=v,e[tot].ne=la[u],la[u]=tot++;}  
int rt[maxn];  
struct Segment_Tree{  
    int mem;  
    struct node{  
        int l,r,v;  
    }t[maxn*logn];  
    void init(){mem=0;}  
    inline void update(int &o,int l,int r,int ql,int qr,int v){  
        if(!o)o=++mem;  
        if(ql<=l&&r<=qr){t[o].v+=v;return;}  
        int mid=(l+r)>>1;  
        if(qr<=mid)update(t[o].l,l,mid,ql,qr,v);  
        else if(ql>mid)update(t[o].r,mid+1,r,ql,qr,v);  
        else update(t[o].l,l,mid,ql,qr,v),update(t[o].r,mid+1,r,ql,qr,v);  
    }  
    inline int merge(int x,int y){  
        if(!x||!y)return x+y;  
        t[x].v+=t[y].v;  
        t[x].l=merge(t[x].l,t[y].l);  
        t[x].r=merge(t[x].r,t[y].r);  
        return x;  
    }  
    inline int query(int o,int l,int r,int x){  
        if(!o||!x)return 0;  
        int mid=(l+r)>>1;  
        if(x<=mid)return t[o].v+query(t[o].l,l,mid,x);  
        else return t[o].v+query(t[o].r,mid+1,r,x);  
    }  
}tr;  
void init(){  
    tot=0;get(n);tr.init();  
    memset(rt,0,sizeof(rt));  
    memset(la,-1,sizeof(la));  
    for(int i=1;i<=n;++i){  
        int fa;get(a[i]);get(fa);  
        tmp[i]=a[i],add(fa,i);  
    }  
    sort(tmp+1,tmp+n+1);  
    cnt=unique(tmp+1,tmp+n+1)-(tmp+1);  
    for(int i=1;i<=n;++i)a[i]=lower_bound(tmp+1,tmp+cnt+1,a[i])-tmp;  
}  
inline void dfs(int u){  
    for(int i=la[u];~i;i=e[i].ne){  
        int v=e[i].v;dfs(v);  
        rt[u]=tr.merge(rt[u],rt[v]);  
    }  
    int v=tr.query(rt[u],1,cnt,a[u]-1)+1;  
    if(v<=tr.query(rt[u],1,cnt,a[u]))return;  
    int l=a[u],r=cnt+1,mid=(l+r)>>1;  
    while(l<r){  
        if(tr.query(rt[u],1,cnt,mid)<v)l=mid+1;  
        else r=mid;  
        mid=(l+r)>>1;  
    }  
    tr.update(rt[u],1,cnt,a[u],l-1,1);  
}  
void solve(){  
    dfs(1);  
    printf("%d\n",tr.query(rt[1],1,cnt,cnt));  
}  
int main(){  
    init();  
    solve();  
    return 0;  
}  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值