[CF960F] Pathwalks

问题描述

You are given a directed graph with nn nodes and mm edges, with all edges having a certain weight.

There might be multiple edges and self loops, and the graph can also be disconnected.

You need to choose a path (possibly passing through same vertices multiple times) in the graph such that the weights of the edges are in strictly increasing order, and these edges come in the order of input. Among all such paths, you need to find the the path that has the maximum possible number of edges, and report this value.

Please note that the edges picked don't have to be consecutive in the input.

输入格式

The first line contains two integers n and m ( 1<=n<=100000 , 1<=m<=100000 ) — the number of vertices and edges in the graph, respectively.

m lines follows.

The i -th of these lines contains three space separated integers ai , bi and wi (1<=ai,bi<=n ,0<=wi<=100000 ), denoting an edge from vertex ai to vertex bi having weight wi.

输出格式

Print one integer in a single line — the maximum number of edges in the path.

题目大意

求有向图上最长的路径满足边权和出现的时间都递增。

解析

不妨按照时间顺序加边(就是输入顺序),下面只要满足边权递增即可。原图为有向图,联想到LIS,我们也可以用类似的方式来做。设\(f[i][w]\)表示以i结尾的且最后一条边权值为w的满足要求的路径最长是多少。那么对于每条边\((u,v,w)\),我们有如下状态转移:
\[ f[v][w]=max(f[u][w1])+1,w1<w \]
考虑如何优化。我们可以对每个节点维护一棵权值线段树,维护\(f[u][1~100000]\)的值。那么,每次转移时,都只需要在u节点的线段树上查询区间\([1,w-1)\)的最大值,同时单点更新v节点的线段树。注意要动态开点。

代码

#include <iostream>
#include <cstdio>
#define N 100002
using namespace std;
struct node{
    int l,r,dat;
}t[N*40];
int n,m,i,f[N],root[N],cnt,ans;
int read()
{
    char c=getchar();
    int w=0;
    while(c<'0'||c>'9') c=getchar();
    while(c<='9'&&c>='0'){
        w=w*10+c-'0';
        c=getchar();
    }
    return w;
}
void change(int &p,int l,int r,int x,int val)
{
    if(p==0) p=++cnt;
    if(l==r){
        t[p].dat=val;
        return;
    }
    int mid=(l+r)/2;
    if(x<=mid) change(t[p].l,l,mid,x,val);
    else change(t[p].r,mid+1,r,x,val);
    t[p].dat=max(t[t[p].l].dat,t[t[p].r].dat);
}
int ask(int p,int l,int r,int ql,int qr)
{
    if(p==0) return 0;
    if(ql<=l&&r<=qr) return t[p].dat;
    int mid=(l+r)/2,ans=0;
    if(ql<=mid) ans=max(ans,ask(t[p].l,l,mid,ql,qr));
    if(qr>mid) ans=max(ans,ask(t[p].r,mid+1,r,ql,qr));
    return ans;
}
int main()
{
    n=read();m=read();
    for(i=1;i<=m;i++){
        int u=read(),v=read(),w=read()+1;
        f[v]=ask(root[u],1,100000,1,w-1)+1;
        ans=max(ans,f[v]);
        change(root[v],1,100000,w,f[v]);
    }
    printf("%d\n",ans);
    return 0;
}

反思

由于原图是有向图,可以首先按照时间顺序加边保证时间递增,再用类似于线段树优化LIS的方式,对每一个点建立线段树优化DP。同样是多阶段转移优化。

有以下两个点需要注意:

  • 每次更新f时都需要更新答案。
  • 由于有0边的存在,可以给所有边权都加1。

猜你喜欢

转载自www.cnblogs.com/LSlzf/p/11745858.html
今日推荐