冷静 清醒 直视内心|

艾特玖

园龄:3年11个月粉丝:12关注:7

Treeland Tour

Treeland Tour

题目大意

给出一棵带点权树,选出一条简单路径,使得其上的最长上升子序列的长度最大。

分析

这题其实数据范围不大,是可以O(n2)做的。但是我们讲的是线段树合并的做法,时间复杂度更优异一些。O(nlogn)

解法本质是用线段树合并优化dp转移过程。

我们先来分析dp

我们设f[i][0/1]:表示以i为根的子树中,以i结尾/开始的最长上升子序列的长度。

我们设ans表示最终答案。我们来看对一个点u来说,我们如何用它去更新答案,并且我们的线段树中需要维护什么。

首先我们来看,答案会来自哪几部分。假设值的最大区间为[1,R]

直接来自f[u][0/1]

考虑这一部分,则我们需要考虑,如何更新f[u][0/1],就像我们平常考虑求最长上升子序列一样,我们如果直接枚举子树中所有节点然后来写,其实也是可以的。但是我们既然用了线段树,那就直接使得维护的权值线段树中,维护的值为mx0i,mx1i表示的即为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的话,我们在遍历子树并合并的过程中,不断查询的mx0mx1,我们直接在外面进行维护一下mx0mx1。则对于当前遍历的子树,我们已经知道了前面所有子树的[1,w[u]-1]中的mx0,与[w[u]+1,R]中的mx1,只需要与当前子树查出的mx0mx1,去更新答案即可。我们设当前子树查出的mx0mx1t0t1。则更新答案为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;
}

本文作者:艾特玖

本文链接:https://www.cnblogs.com/aitejiu/p/16861285.html

版权声明:本作品采用艾特玖许可协议进行许可。

posted @   艾特玖  阅读(40)  评论(0编辑  收藏  举报
历史上的今天:
2021-11-05 P2419
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起