题目背景

缩点+DP

题目描述

给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入输出格式

输入格式:

第一行,n,m

第二行,n个整数,依次代表点权

第三至m+2行,每行两个整数u,v,表示u->v有一条有向边

输出格式:

共一行,最大的点权之和。

输入输出样例

输入样例#1: 复制

2 2
1 1
1 2
2 1
输出样例#1: 复制

2

说明

n<=10^4,m<=10^5,0<=点权<=1000

算法:Tarjan缩点+DAGdp、

基本可以算是套路了吧,先缩点,然后tpsort,跑dp,是不是可以解决不少图论题目呢

思路:

两个数组,dfn,low。

dfn:dfs序(时间戳)

low:以u为根的子树里dfn最小的那个点(它的最早祖先)

附属数组:

st:模拟栈

co:重建图的联通快(点)

维护这两个数组,当dfn[u]=low[u]时判定为强连通分量(环)

为什么呢?

当一个点它的最老祖先等于它自己的时候,这就是一个环啊

了解四种边:

树枝边:遍历路径

前向边:爹——>儿

后向边:儿——>爹

横插边:从这个子树插到另外一个搜索子树的边

下面介绍怎么维护low

如果(u,v)是树枝边,一切好说,直接比较low[u]和low[v]的最小值即可,因为v是u的儿子,直接比较它们最早祖先的大小。

如果(u,v)是后向边或者横插边,就需要比较lou[u]和dfn[v]的最小值。

为什么?

后向边相对好理解,从这个点可以回溯到它的祖先,我们需要比较它儿子们的时间戳最小值和它祖先的时间戳 的最小值。

若之前搜到过u的祖先,那么它祖先的dfn一定是小的,但是我能从它的耳孙之间找到它的身影(自交?回交?)!

这说明什么?强连通分量!

但是,不要着急,我们需要找到强连通分量的根。所以我们需要比较一个极小值。

解释通了,那么横插边也是同理。

当dfn=low时:

也就是说它的子孙的最高祖先就是子孙本身时。

dfn时间戳正好是它子树节点的low的最小值。因为dfn值具有不重复性,所以可以断定,以它为根的子树的所有点都是一个强连通分量。

所以,可以很好地判断图的环。

注意:因为图可能不连通,所以要多次跑tarjan。

时间复杂度:由于每个点只遍历了一次,每条边也只遍历了一次,所以O(N+M)(不是spfa那么不靠谱,人家就是N+M)

给出缩点板子的代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=;
struct node
{
int next,to;
}e[maxn];
int head[maxn],cnt,sum[maxn],a[maxn];
int n,m,ru[maxn];
inline void addedge(int from,int to)
{
e[++cnt].next=head[from];
e[cnt].to=to;
head[from]=cnt;
}
int dep,top;
int dfn[maxn],low[maxn],vis[maxn],co[maxn],st[maxn];
void tarjan(int u)
{
dfn[u]=low[u]=++dep;
vis[u]=;
st[++top]=u;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[v],low[u]);
}
else if(vis[v])
{
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u])//退栈,把强连通分量薅出来
{
int t;
do
{
t=st[top--];
sum[u]+=a[t];
co[t]=u;
vis[t]=;
}while(t!=u);
}
}
int dp[maxn];
queue < int > q;
void tpsort()
{
for(int i=;i<=n;i++)
{
if(ru[i]==&&co[i]==i)
q.push(i);
dp[co[i]]=sum[co[i]];
}
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
dp[v]=max(dp[u]+sum[co[v]],dp[v]);
if(!(--ru[v]))
{
q.push(v);
}
}
}
} pair < int , int > g[maxn];
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);
g[i].first=x;
g[i].second=y;
}
for(int i=;i<=n;i++)
{
if(!dfn[i])
tarjan(i);
}
memset(e,,sizeof(e));
memset(head,,sizeof(head));
cnt=;
for(int i=;i<=m;i++)
{
int x=g[i].first;
int y=g[i].second;
if(co[x]!=co[y])
{
addedge(co[x],co[y]);
ru[co[y]]++;
}
}
tpsort();
int ans=-;
for(int i=;i<=n;i++)
{
ans=max(ans,dp[i]);
}
printf("%d",ans);
return ;
}

无向图:

void tarjan(int u)
{
dfn[u]=low[u]=++tot;
st[++top]=u;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(!vis[i])
{
vis[i]=vis[i^]=;
if(dfn[v]==)
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else
{
low[u]=min(low[u],dfn[v]);
}
}
}
if(dfn[u]==low[u])
{
color[u]=++col;
while(st[top]!=u)
color[st[top]]=col,top--;
top--;
}
}

(完)

tarjan学习(复习)笔记(持续更新)(各类找环模板)的更多相关文章

  1. OpenFlow1.3.3 学习记录(持续更新)

    OpenFlow1.3.3 学习记录(持续更新) 正在学习OpenFlow1.3,该篇笔记将日常更新,主要内容大致为官方文档的总结与翻译. 交换机组件 按照优先级顺序进行包匹配,如果匹配到流表项,则执 ...

  2. BLE资料应用笔记 -- 持续更新

    BLE资料应用笔记 -- 持续更新 BLE 应用笔记 小书匠 简而言之,蓝牙无处不在,易于使用,低耗能和低使用成本.'让我们'更深入地探索这些方面吧. 蓝牙无处不在-,您可以在几乎每一台电话.笔记本电 ...

  3. [读书]10g/11g编程艺术深入体现结构学习笔记(持续更新...)

    持续更新...) 第8章 1.在过程性循环中提交更新容易产生ora-01555:snapshot too old错误.P257 (这种情况我觉得应该是在高并发的情况下才会产生) 假设的一个场景是系统一 ...

  4. 痞子衡嵌入式:史上最强i.MX RT学习资源汇总(持续更新中...)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MX RT学习资源. 类别 资源 简介 官方汇总 i.MXRT产品主页 恩智浦官方i.MXRT产品主页,最权威的资料都在这里,参考手 ...

  5. 2020/1/29 PHP代码审计之进一步学习XSS【持续更新】

    0x00 上午学习了XSS漏洞,中午吃饭想了想,还是思考的太浅层了,这种老生常谈的东西对于现在的我意义不大.现在我需要的是思考.于是就有了这个随笔.在本文中,我会持续更新一些XSS的深入思考,payl ...

  6. react-native-storage 使用笔记 持续更新

    React-native-storage是在AsyncStorage之上封装的一个缓存操作插件库,刚开始接触这个也遇到了一些问题,在这里简单记录总结一下,碰到了就记下来,持续更新吧 1.安卓下stor ...

  7. 数据分析之Pandas和Numpy学习笔记(持续更新)<1>

    pandas and numpy notebook        最近工作交接,整理电脑资料时看到了之前的基于Jupyter学习数据分析相关模块学习笔记.想着拿出来分享一下,可是Jupyter导出来h ...

  8. Semantic ui 学习笔记 持续更新

    这个semantic 更新版本好快~ 首先是代码的标识<code></code> 具体样式就是红框这样的 圈起来代码感觉不错 不过要在semantic.css里在加上如下样式~ ...

  9. Git学习笔记(持续更新)

    1.强制同步为远程的代码 远程仓库回退了commit的情况下(第2条描述之情况),强制同步远程的代码到本地 #更新远程最新的所有代码,但是不merge或者rebase git fetch --all ...

随机推荐

  1. 洛谷 P4170 [CQOI2007]涂色

    题目描述 假设你有一条长度为5的木版,初始时没有涂过任何颜色.你希望把它的5个单位长度分别涂上红.绿.蓝.绿.红色,用一个长度为5的字符串表示这个目标:RGBGR. 每次你可以把一段连续的木版涂成一个 ...

  2. C#学习--SQL server数据库基本操作(连接、增、删、改、查)封装

    写在前面: 在日常的工作中,通常一个项目会大量用的数据库的各种基本操作,因此小编几个常见的数据库的操作封装成了一个dll方便后续的开发使用.SQLserver数据库是最为常见的一种数据库,本文则主要是 ...

  3. bs4 UnicodeEncodeError: 'gbk' codec can't encode character '\xa0'

    Problem: 写爬虫时,出现了以下错误: 意思是Unicode编码错误,gbk编解码器不能编码\xa0字符. 爬取信息包含中文,使用BeautifulSoup库解析网页,用get_text()方法 ...

  4. postman环境变量设置

    1.点击小齿轮进入到环境变量添加页面,点击add添加环境变量 2.输入变量名称和变量值 3.添加成功 4.接口中设置变量

  5. 让你如“老”绅士般编写 Python 命令行工具的开源项目:docopt

    作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...

  6. Ubuntu 16.04 集成安装Apache+PHP+Kerberos+LDAP+phpLDAPadmin

    一.安装Apache 1.1.安装Apache apt-get update apt-get install apache2 过程如下: root@duke01:~# apt-get update命中 ...

  7. Axios 详解

    首先祝广大程序猿们节日快乐! 一.axios简介 基于promise,用于浏览器和node.js的http客户端 二.特点 支持浏览器和 node.js 支持 promise 能拦截请求和响应 能转换 ...

  8. JVM 垃圾收集与内存分配

    判断对象是否还活着 引用计数法 给对象添加引用计数器,添加加1,引用失效减1,如果为0就是不可使用的.问题是不能解决互相引用带来的问题 可达性分析法 以GC Roots为起点,判断到一个对象是否有引用 ...

  9. QLable 显示图片

    1,各种对就是不显示,因为路径中有其它符号如\n\r什么的 QStringList FileOpeartion::PathCombine (const QString strPath, QString ...

  10. Spring Cloud 网关服务 zuul 三 动态路由

    zuul动态路由 网关服务是流量的唯一入口.不能随便停服务.所以动态路由就显得尤为必要. 数据库动态路由基于事件刷新机制热修改zuul的路由属性. DiscoveryClientRouteLocato ...