首页 > 其他分享 >Treeland Tour

Treeland Tour

时间:2022-11-05 21:14:04浏览次数:68  
标签:return Treeland int max tr mx0 mx1 Tour

Treeland Tour

题目大意

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

分析

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

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

我们先来分析dp

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

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

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

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

考虑这一部分,则我们需要考虑,如何更新f[u][0/1],就像我们平常考虑求最长上升子序列一样,我们如果直接枚举子树中所有节点然后来写,其实也是可以的。但是我们既然用了线段树,那就直接使得维护的权值线段树中,维护的值为\(mx0_i,mx1_i\)表示的即为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;
}

标签:return,Treeland,int,max,tr,mx0,mx1,Tour
From: https://www.cnblogs.com/aitejiu/p/16861285.html

相关文章

  • CF487E Tourists(Tarjan,圆方树,树链剖分,线段树)
    CF487ETourists带权无向图\(N,M\),\(Q\)次询问\(s,t\)所有不经过重复点可能路径经过的最小值的最小值。CODE每次修改一个圆点\(u\)周围的方点有点难。可是李神......
  • 9.CF490F Treeland Tour 线段树合并
    9.CF490FTreelandTour线段树合并给出一棵带点权树,求树上最长上升子序列的长度对每个点开两棵线段树,记录叶节点到当前节点的LIS和LDS,然后合并时取最大值即可洛谷传送门:​......
  • POJ-1637 Sightseeing tour
    Sightseeingtour网络流-最大流考虑欧拉图,无向边:度数全为偶数,有向边:出度和入度相等对于有向边来说,已经不可更改;对于无向边来说,可以更改其指向因此这题最终就是想找一......
  • Tournament Result
    ProblemStatement$N$playersplayedaround-robintournament.Youaregivenan$N$-by-$N$table$A$containingtheresultsofthematches.Let$A_{i,j}$deno......
  • 814 C Fighting Tournament
    写的时候思路想到了,但是不怎么会维护。这儿贴一个比较好理解的维护方式,用的双端队列。#include<bits/stdc++.h>usingnamespacestd;#defineintlonglong#pragma......
  • OpenCV(一) | contourArea()求得的面积是哪里的面积?
    ​本文来自公众号”AI大道理“。这里即有AI,也有大道理。 1、问题描述:轮廓的面积contourArea()得出一个面积,后面利用宽*高得出一个面积,两个面积结果不一样。统计......
  • CF1719C Fighting Tournament 题解
    思路根据题意,很容易看出,每个人都完成一次比赛后,即完成\(n-1\)轮之后,力量值最大的人会留在第一的位置,且在第\(n-1\)轮完成后,除了力量值最大的人,其他人的胜场数都不会再......