CF960F Pathwalks (线段树优化图上的LIS)

这篇博客介绍了如何利用线段树动态开点的方法解决图上的最长大增序路径问题。通过对每个顶点维护一个权值线段树,动态更新每个节点的入边信息,实现边权和编号严格递增的最长路径搜索。算法复杂度为O(nlogn),有效地避免了空间爆炸的问题。
摘要由CSDN通过智能技术生成

传送门

题意:给定无向图,带边权,n个顶点m条边,找到一条最长的路径满足,经过的边边权严格递增,边的编号也严格递增(编号即输入的顺序),最长的定义是边的条数最多。
(1<=n<=1e5,1<=m<=1e5)

相当于把LIS问题放到了图上,我们按边的输入顺序进行更新答案。

  • 我们对每个顶点维护一个权值线段树,(就像用权值线段树维护LIS一样),
    定义域是边权,值就是这个以边权结尾的路径的最长长度,维护区间最大值。
  • 对于边(u,v,w),我们在u的线段树里面查询[0,w-1]最大值M,v线段树的w位置就更新为M+1。
  • 但是如果对每个顶点都建树的话,会爆空间,不过观察到所有树的树链总条数是m条,所以我们用动态开点,rt[i]表示顶点i的线段树的根节点,这就有点借用主席树的思想了,只不过主席树不同版本之间存在树链共用,而这里的动态开点本质是n棵没有关联的线段树,只不过这n棵树都在同一个数组里面申请空间罢了。
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
#define pdd pair<double, double>
#define re register
const int maxn = 1e5 + 10;
const int mx = 40;
const ll mod = 1e9+7;
const ll inf = (ll)4e18+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
//无向图 带边权 定义好的路径为 经过的边边权递增 边序号递增 求最长的好路径长度
//我们按输入顺序处理边
//对于 (u,v,w) 我们需要在u的入边中找到边权在[1,w-1]区间内的答案最大值 进行dp
//对于每个点一个维护权值线段树 动态开点+主席树
//但是这个主席树和一般的主席树不太一样 它并非是在以前的版本上修改
//而是开了n棵独立树 每个顶点都对应一棵树 但是总空间还是n条链  

int tree[maxn*mx];//用于动态开点申请节点空间
int lc[maxn*mx],rc[maxn*mx],tot;
int n,m;
int ans;
int rt[maxn];
inline void pushup(int rt) 
{
    tree[rt]=max(tree[lc[rt]],tree[rc[rt]]);
}
inline void upd(int &rt,int l,int r,int pos,int v) 
{
    if(!rt) rt=++tot;
    if(l==r) 
    {
        tree[rt]=max(tree[rt],v);
        return ;
    }
    int mid=l+r>>1;
    if(pos<=mid) upd(lc[rt],l,mid,pos,v);
    else upd(rc[rt],mid+1,r,pos,v);
    pushup(rt);
}
inline int qy(int rt,int l,int r,int vl,int vr) 
{
    if(!rt || vl>vr) return 0;
    if(vl<=l && r<=vr) return tree[rt];
    int mid=l+r>>1;
    if(vr<=mid) return qy(lc[rt],l,mid,vl,vr);
    else if(vl>mid) return qy(rc[rt],mid+1,r,vl,vr);
    return max(qy(lc[rt],l,mid,vl,vr) , qy(rc[rt],mid+1,r,vl,vr));
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++) 
    {
        int u,v,w;
        scanf("%d %d %d",&u,&v,&w);//加入边 u v w
        int tmp=1+qy(rt[u],0,maxn,0,w-1);//u的入边中 权值位于[0,w-1]的边的答案最大值
        upd(rt[v],0,maxn,w,tmp);//更新v的入边答案 单点更新
        ans=max(ans,tmp);
    }
    cout<<ans<<'\n';
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值