洛谷题单指南-线段树的进阶用法-Pathwalks

原题链接:https://www.luogu.com.cn/problem/CF960F

题意解读:按输入顺序从边中选出能首尾相连且权值递增的最多边数。

解题思路:

依然从最长上升子序列的模型出发,

对于输入的第i条边,用a[i],b[i],w[i]分别表示起点、终点、权值,

设f[i]表示第i条边结束的w的最长上升子序列长度,

则有f[i] = max(f[j]) +1,且需满足j < i, b[j] == a[i], w[j] < w[i]

似乎又是一个类似偏序的问题,第一维用来维护b,第二维用来维护w对应的最长上升子序列maxf

由于第一维每次只做相等判断,只需要用root[N]维护多个线段树的根节点,不需借助树状数组

第二维就是root[i]对应的权值线段树,节点值为w,维护信息为当前边的最长上升子序列maxf

只需要按输入顺序遍历所有边,

对于每条边a[i],b[i],w[i],在root[a[i]]中查询权值<w[i]的最大maxf,记为res,计算当前f[i] = res + 1

然后将root[b[i]]为根的线段树值为w[i]的maxf更新为f[i],不断递推即可求得所有f[i],记录最大值即可。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 100005;

struct Node
{
    int L, R;
    int maxf;
} tr[N * 20];

int root[N], idx;
int a[N], b[N], w[N], maxw;
int f[N]; //f[i]表示以第i条边结束的w的最长上升子序列长度
int n, m, ans;

//在根为u的线段树中查询值<v的最大的maxf
int query(int u, int l, int r, int v)
{
    if(r < v) return tr[u].maxf;
    else if(l >= v) return 0;
    else
    {
        int mid = l + r >> 1;
        return max(query(tr[u].L, l, mid, v), query(tr[u].R, mid + 1, r, v));
    }
}

//将根为u的线段树值v的maxf更新为f,如果f更大的话
int update(int u, int l, int r, int v, int f)
{
    if(!u) u = ++idx;
    tr[u].maxf = max(tr[u].maxf, f);
    if(l == r) return u;
    int mid = l + r >> 1;
    if(v <= mid) tr[u].L = update(tr[u].L, l, mid, v, f);
    else tr[u].R = update(tr[u].R, mid + 1, r, v, f);
    return u;
}

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= m; i++)
    {
        cin >> a[i] >> b[i] >> w[i];
        maxw = max(maxw, w[i]);
    }
    for(int i = 1; i <= m; i++)
    {
        f[i] = query(root[a[i]], 0, maxw, w[i]) + 1;
        ans = max(ans, f[i]);
        root[b[i]] = update(root[b[i]], 0, maxw, w[i], f[i]);
    }
    cout << ans;
    return 0;
}

 

posted @   五月江城  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示