题意:给定无向图,带边权,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;
}