一棵树,对于每个点,求从任何一个在该点的子树为头,以该点为结尾的序列必须选择这个点的最长不降子序列。
首先我们使用权值线段树计算答案每个点 ( l , r , w ) (l,r,w) (l,r,w)表示以 l ∼ r l\sim r l∼r为结尾最长的不降升子序列长度。
然后利用线段树维护,每次跑完子节点之后将线段树合并到父节点上来计算答案。
时间复杂度 O ( n l o g n ) O(n\ log\ n) O(n log n)
#pragma GCC optimize(2)
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
#include
#include
#include
#include
using namespace std;
const int N=101000;
int n,rt[N],tot,ls[N],ans[N],w[N];
struct Edge_node{
int to,next;
}a[N];
struct Tree_node{
int w,l,r,lson,rson;
};
vector<int> q[N],c[N];
struct Line_cut_tree{
Tree_node t[N*20];
int tot;
#define ls t[x].lson
#define rs t[x].rson
int Ask(int x,int l,int r,int L,int R)
{
if(!x) return 0;
if(L==l&&R==r)
return t[x].w;
int mid=(L+R)/2;
if(r<=mid) return Ask(ls,l,r,L,mid);
else if(l>mid) return Ask(rs,l,r,mid+1,R);
else return max(Ask(ls,l,mid,L,mid),Ask(rs,mid+1,r,mid+1,R));
}
void Change(int &x,int pos,int z,int L,int R)
{
if(!x) x=++tot;
if(L==R){
t[x].w=max(z,t[x].w);
return;
}
int mid=(L+R)/2;
if(pos<=mid) Change(ls,pos,z,L,mid);
else if(pos>mid) Change(rs,pos,z,mid+1,R);
t[x].w=max(t[ls].w,t[rs].w);
}
int merge(int x,int y,int L,int R)
{
if(!x||!y)
return x+y;
t[x].w=max(t[x].w,t[y].w);
if(L==R)
return x;
int mid=(L+R)/2;
t[x].lson=merge(t[x].lson,t[y].lson,L,mid);
t[x].rson=merge(t[x].rson,t[y].rson,mid+1,R);
return x;
}
#undef ls
#undef rs
}Tree;
void addl(int x,int y)
{
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;
}
void dfs(int x)
{
int root=0;
for(int i=ls[x];i;i=a[i].next)
{
int y=a[i].to;
dfs(y);
root=Tree.merge(root,rt[y],1,n);
}
ans[x]=Tree.Ask(root,1,w[x],1,n)+1;
Tree.Change(root,w[x],ans[x],1,n);
rt[x]=root;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
if(i==1) continue;
addl(x,i);
}
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
dfs(1);
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
}