法法塔的奖励

题目大意

给定一颗树,求任意结点子树内任意叶子节点到其路径包括该节点的最长不下降子序列的长度的最大值。

你可以搞dfs序
你可以搞Treap的启发式合并
你可以搞线段树合并

#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
int root[maxn],tree[maxn*20],left[maxn*20],right[maxn*20];
int a[maxn],h[maxn],go[maxn],next[maxn],ans[maxn];
int i,j,k,l,t,n,m,tot;
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void change(int &x,int l,int r,int a,int b){
    if (!x) x=++tot;
    if (l==r){
        tree[x]=max(tree[x],b);
        return;
    }
    int mid=(l+r)/2;
    if (a<=mid) change(left[x],l,mid,a,b);else change(right[x],mid+1,r,a,b);
    tree[x]=max(tree[left[x]],tree[right[x]]);
}
int query(int x,int l,int r,int a,int b){
    if (!x) return 0;
    if (l==a&&r==b) return tree[x];
    int mid=(l+r)/2;
    if (b<=mid) return query(left[x],l,mid,a,b);
    else if (a>mid) return query(right[x],mid+1,r,a,b);
    else return max(query(left[x],l,mid,a,mid),query(right[x],mid+1,r,mid+1,b));
}
int merge(int a,int b,int l,int r){
    if (!a||!b) return a+b;
    if (l==r){
        tree[a]=max(tree[a],tree[b]);
        return a;
    }
    int mid=(l+r)/2;
    left[a]=merge(left[a],left[b],l,mid);
    right[a]=merge(right[a],right[b],mid+1,r);
    tree[a]=max(tree[a],tree[b]);
    return a;
}
void solve(int x){
    int t=h[x],j=0;
    while (t){
        solve(go[t]);
        j=merge(j,root[go[t]],1,n);
        t=next[t];
    }
    ans[x]=query(j,1,n,1,a[x])+1;
    change(j,1,n,a[x],ans[x]);
    root[x]=j;
}
int main(){
    scanf("%d",&n);
    fo(i,1,n){
        scanf("%d",&t);
        if (t!=-1) add(t,i);
    }
    fo(i,1,n) scanf("%d",&a[i]);
    tot=0;
    solve(1);
    fo(i,1,n) printf("%d ",ans[i]);
}

你可能感兴趣的:(线段树,treap,dfs序)