• [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
    ",ans);
    	return 0;
    }
    

    反思

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

    有以下两个点需要注意:

    • 每次更新f时都需要更新答案。
    • 由于有0边的存在,可以给所有边权都加1。
  • 相关阅读:
    jS Ajax 上传文件报错"Uncaught TypeError: Illegal invocation"
    layer实现关闭弹出层刷新父界面功能详解
    PHP开发APP接口简单签名全过程(二)实际测试
    PHP开发APP接口简单签名全过程(一)
    Laravel 避免 Trying to get property of non-object 错误的六种方法 [新增第六种 data_get]
    在PHP代码中将HTML代码原样输出的方式
    在使用 Laravel Eloquent 模型时,我们要判断取出的结果集是否为空,但我们发现直接使用 is_null 或 empty是无法判段它结果集是否为空的!!!
    使用layer的iframe层提交表单后,需要关闭当前的iframe层,然后刷新父页面的方法
    laravel中如何实现验证码验证及使用
    递归思想的由来
  • 原文地址:https://www.cnblogs.com/LSlzf/p/11745858.html
Copyright © 2020-2023  润新知