水友赛(树上LIS)

水友赛(树上\(LIS\)

题目描述

银河战舰 $ DK $ 战队计划在 $ T $ 国组织一系列水友赛。$ T $ 国有 \(n\) 个城市,通过 \(n-1\) 条双向航线连接(形成一棵树),所有城市互相可达。

规则说明

  1. 行程可以从任意城市开始,任意城市结束,但整个路线不能重复经过同一个城市(即路线是简单路径)。
  2. 并非每个经过的城市都必须举行水友赛。
  3. 每次举行水友赛的城市,其人气指数必须严格高于上一次举行水友赛的城市的人气指数。

目标
求最多可以举行多少场水友赛(即满足严格递增人气序列的最大长度)。

输入格式

  • 第一行:整数 \(n\)(城市数)
  • 第二行:\(n\) 个整数,表示每个城市的人气指数
  • 接下来 \(n-1\) 行:每行两个整数 \(a\),\(b\),表示一条双向航线

输出格式

一行,表示最多举行水友赛的次数

数据范围

  • \(2 \leq n \leq 200,000\)
  • \(1 \leq 人气指数 \leq 10^6\)

解题报告

感谢同机房大佬的博客指导XiaoZi_qwq

题目大意:求解所有的树上有向简单路径对应的序列的最长$ LIS $,输出其中长度的最大值

一开始想到的是点分治,遍历每一个节点 $ u $,计算每个包含节点 $ u $的序列的 $ LIS $,可以维护所有的路径序列

因为这相当于将所有路径按照其起点和终点的 $ LCA $ 分类,这样做显然是不重不漏的

那么,我们就考虑如何在一点统计经过这个点的所有路径的价值

先考虑我们所统计的方式的一个特点我们基本上是在一条路径的中间某个点统计的,少数是在两端,可以统一情况

那么,对于一条$ LIS \(,从中间一点来看,左边是以其为结尾的\)LIS\(,右边是以其为开头的\)LIS$

为了方便处理,我们将右边转化为以其为结尾的\(LDS\)

于是我们可以想到:维护以这个点为终点的序列中每一个点权为结尾的最长严格递增/递减序列长度,然后想个方法正确维护

先考虑我们如何在链上求解 $ LIS $,两个思路:贪心&二分 或 线段树(树状数组)优化

我没试过 把第一种思路扩展到树上,所以我们尝试第二种思路\((doge)\)

于是我们在每个节点 \(u\) 建立一颗线段树,这颗线段树以权值为下标,维护经过这个节点 \(u\)的路径中以每个权值做结尾的最长\(LIS\)\(LDS\)

先来考虑如果对于每个节点都已经维护好线段树,怎么统计出答案?

由于经过节点\(u\)的路径的序列都可以这样分:

\(v_1,v_2\)\(u\)的两个不同的儿子节点,一条经过\(u\)的序列的为“ 以\(v_1\)结尾的路径 \(\rightarrow u \rightarrow\)\(v_2\)开头的路径 ”

再结合之前的分析,若求一个经过\(u\)的序列的\(LIS\)

就可以分为一个以\(u\)为结尾的序列的\(LIS\)与以一个不在这一路径上的子节点\(v\)为结尾的\(LDS\)归并

注意不是简单的将两段首尾相接,因为不保证\(LDS\)的元素可以全部大于\(LIS\)

所以我们需要对每一个权值都其计算经过\(u\)的序列的\(LIS\)和以一个不在这一路径上的子节点\(v\)为结尾的\(LDS\)

这两个值都在\(u\)\(v\)的线段树中维护了

那么现在来考虑怎么维护这个线段树

对于一个节点\(u\),应该计算其子节点的最优情况,再计算包含它自己的情况,取最优

同样的,我们应该对于每个权值都更新一遍

而处理子节点线段树对父节点线段树的贡献的问题有两个思路:树上启发式合并 和 线段树合并

这里明显可以线段树合并,并在合并中完成答案的计算。

这道题基本解决

具体要注意的是

1.从 \(u \rightarrow v\) 和 $v \rightarrow u $是不同的,合并的时候要分别计算

2.对于点\(u\)应该分取和不取计算

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=2001100;

inline int read()
{
	int f=1,x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch))  { x=x*10+ch-'0';    ch=getchar(); }
	return f*x;
}

vector<int> e[N];
int n,w[N];
int NUM[N],top;

#define ls(x) T[x].ls
#define rs(x) T[x].rs
#define mid ( (l+r)>>1 )
#define rt(x) root[x]
struct tree
{
    int ls,rs;
    int val[2];
}T[N<<2];
int root[N],cnt;

inline void pushup(int i,bool id)
{ T[i].val[id]=max(T[ls(i)].val[id],T[rs(i)].val[id]); }

void update(int &i,int l,int r,int pos,int val,bool id)
{
    if(!i) i=++cnt;
    if(l==r)
    {
        T[i].val[id]=max(T[i].val[id],val);
        return ;
    }
    if(pos<=mid) update(ls(i),l,mid,pos,val,id);
    if(pos>mid)  update(rs(i),mid+1,r,pos,val,id);
    pushup(i,id);
}

int query(int i,int l,int r,int L,int R,bool id)
{
    if(!i) return 0;
    if(L<=l && r<=R) return T[i].val[id];
    int tmp=0;
    if(L<=mid) tmp=max(tmp,query(ls(i),l,mid,L,R,id) );
    if(R>mid)  tmp=max(tmp,query(rs(i),mid+1,r,L,R,id) );
    return tmp;
}

int ans;

void Merge(int &i,int j,int l,int r)
{
    if(!i || !j) return (void)(i+=j);
    ans=max(ans, T[ls(i)].val[0]+T[rs(j)].val[1] );
    ans=max(ans, T[ls(j)].val[0]+T[rs(i)].val[1] );
    if(l==r)
    {
        T[i].val[0]=max(T[i].val[0],T[j].val[0]);
        T[i].val[1]=max(T[i].val[1],T[j].val[1]);
        return ;
    }
    Merge(ls(i),ls(j),l,mid);
    Merge(rs(i),rs(j),mid+1,r);
    pushup(i,0),pushup(i,1);
}

void dfs(int u,int fa)
{
    int W1=0,W2=0;
    for(auto v:e[u])
    {
        if(v==fa) continue;
        dfs(v,u);
        int w1=query(rt(v),1,top,1,w[u]-1,0);
        int w2=query(rt(v),1,top,w[u]+1,top,1);
        ans=max(ans, max(W1+w2+1,W2+w1+1) );
        W1=max(W1,w1),W2=max(W2,w2);
        Merge(rt(u),rt(v),1,top);
    }
    update(rt(u),1,top,w[u],W1+1,0);
    update(rt(u),1,top,w[u],W2+1,1);
}

signed main()
{
	freopen("lis.in","r",stdin);
	freopen("lis.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++)
      NUM[++top]=( w[i]=read() );
    sort(NUM+1,NUM+top+1);
    top=unique(NUM+1,NUM+top+1)-NUM-1;
    for(int i=1;i<=n;i++)
      w[i]=lower_bound(NUM+1,NUM+top+1,w[i])-NUM;
    for(int i=1;i<n;i++)
    {
        int u=read(),v=read();
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(1,0);
    printf("%d",ans);
	return 0;
}


posted @ 2025-08-25 19:01  南北天球  阅读(8)  评论(0)    收藏  举报