Treeland Tour
Treeland Tour
题目大意
给出一棵带点权树,选出一条简单路径,使得其上的最长上升子序列的长度最大。
分析
这题其实数据范围不大,是可以做的。但是我们讲的是线段树合并的做法,时间复杂度更优异一些。。
解法本质是用线段树合并优化dp
转移过程。
我们先来分析dp
。
我们设f[i][0/1]:表示以i为根的子树中,以i结尾/开始的最长上升子序列的长度。
我们设ans
表示最终答案。我们来看对一个点u
来说,我们如何用它去更新答案,并且我们的线段树中需要维护什么。
首先我们来看,答案会来自哪几部分。假设值的最大区间为[1,R]
。
直接来自f[u][0/1]
。
考虑这一部分,则我们需要考虑,如何更新f[u][0/1]
,就像我们平常考虑求最长上升子序列一样,我们如果直接枚举子树中所有节点然后来写,其实也是可以的。但是我们既然用了线段树,那就直接使得维护的权值线段树中,维护的值为表示的即为i
这个值在子树中的以它结束/开始的最长上升子序列的长度。
- 我们询问区间
[1,w[u]-1]
中的mx0
,来更新f[u][0]
。 - 我们询问区间
[w[u]+1,R]
中的mx1
,来更新f[u][1]
。
最长的子序列的两头分别在两个子树中,且其中无u
。
其中无u
的话,我们可以在合并线段树的过程中。找到对于当前区间[l,r]
来说,其前面[1,l-1]
的mx0
。则更新答案为ans=max(ans,tr[u].mx1+mx0)
。
最长的子序列的两头分别在两个子树中,且其中有u
。
有u
的话,我们在遍历子树并合并的过程中,不断查询的mx0
,mx1
,我们直接在外面进行维护一下mx0
,mx1
。则对于当前遍历的子树,我们已经知道了前面所有子树的[1,w[u]-1]
中的mx0
,与[w[u]+1,R]
中的mx1
,只需要与当前子树查出的mx0
与mx1
,去更新答案即可。我们设当前子树查出的mx0
与mx1
为t0
,t1
。则更新答案为ans = max({ans,mx0+t1+1,mx1+t0+1})
。
结束啦。
AC_code
#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
const int N = 6010,M = N<<1;
struct Node
{
int l,r;
int mx0,mx1;
}tr[N*30];
int root[N],cnt;
int ans,n,R;
int h[N],ne[M],e[M],idx;
int f[N][2],r[N];
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void pushup(int u)
{
tr[u].mx0 = max(tr[tr[u].l].mx0,tr[tr[u].r].mx0);
tr[u].mx1 = max(tr[tr[u].l].mx1,tr[tr[u].r].mx1);
}
int merge(int u,int v,int l,int r,int mx10,int mx20)
{
if(!u)
{
ans = max(mx10+tr[v].mx1,ans);
return v;
}
if(!v)
{
ans = max(mx20+tr[u].mx1,ans);
return u;
}
if(l==r)
{
ans = max(mx10+tr[v].mx1,ans);ans = max(mx20+tr[u].mx1,ans);
tr[u].mx0 = max(tr[u].mx0,tr[v].mx0);tr[u].mx1 = max(tr[u].mx1,tr[v].mx1);
return u;
}
int mid = l + r >> 1;
tr[u].r = merge(tr[u].r,tr[v].r,mid+1,r,max(mx10,tr[tr[u].l].mx0),max(mx20,tr[tr[v].l].mx0));
tr[u].l = merge(tr[u].l,tr[v].l,l,mid,mx10,mx20);
pushup(u);
return u;
}
int query0(int u,int l,int r,int L,int R)
{
if(r<l) return 0;
if(R<l||L>r) return 0;
if(!u) return 0;
if(L<=l&&r<=R) return tr[u].mx0;
int mid = l + r >> 1;
return max(query0(tr[u].l,l,mid,L,R),query0(tr[u].r,mid+1,r,L,R));
}
int query1(int u,int l,int r,int L,int R)
{
if(r<l) return 0;
if(R<l||L>r) return 0;
if(!u) return 0;
if(L<=l&&r<=R) return tr[u].mx1;
int mid = l + r >> 1;
return max(query1(tr[u].l,l,mid,L,R),query1(tr[u].r,mid+1,r,L,R));
}
void modify(int &u,int l,int r,int x,int k)
{
if(!u) u = ++cnt;
if(l==r)
{
tr[u].mx0 = max(f[k][0],tr[u].mx0);
tr[u].mx1 = max(f[k][1],tr[u].mx1);
return ;
}
int mid = l + r >> 1;
if(x<=mid) modify(tr[u].l,l,mid,x,k);
else modify(tr[u].r,mid+1,r,x,k);
pushup(u);
}
void dfs(int u,int fa)
{
f[u][0] = f[u][1] = 1;
int mx0 = 0,mx1 = 0;
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==fa) continue;
dfs(j,u);
int t0 = query0(root[j],1,R,1,r[u]-1),t1 = query1(root[j],1,R,r[u]+1,R);
f[u][0] = max(f[u][0],t0 + 1);f[u][1] = max(f[u][1],t1 + 1);
ans = max({ans,mx0+t1+1,mx1+t0+1});
mx0 = max(mx0,t0),mx1 = max(mx1,t1);
root[u] = merge(root[u],root[j],1,R,0,0);
}
modify(root[u],1,R,r[u],u);
}
int main()
{
ios;
cin>>n;memset(h,-1,sizeof h);
for(int i=1;i<=n;i++) cin>>r[i],R = max(R,r[i]);
for(int i=0;i<n-1;i++)
{
int u,v;cin>>u>>v;
add(u,v),add(v,u);
}
dfs(1,-1);
cout<<ans<<'\n';
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2021-11-05 P2419