tarjan学习(复习)笔记(持续更新)(各类找环模板)
题目背景
缩点+DP
题目描述
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入输出格式
输入格式:
第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
输出格式:
共一行,最大的点权之和。
输入输出样例
说明
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学习(复习)笔记(持续更新)(各类找环模板)的更多相关文章
- OpenFlow1.3.3 学习记录(持续更新)
OpenFlow1.3.3 学习记录(持续更新) 正在学习OpenFlow1.3,该篇笔记将日常更新,主要内容大致为官方文档的总结与翻译. 交换机组件 按照优先级顺序进行包匹配,如果匹配到流表项,则执 ...
- BLE资料应用笔记 -- 持续更新
BLE资料应用笔记 -- 持续更新 BLE 应用笔记 小书匠 简而言之,蓝牙无处不在,易于使用,低耗能和低使用成本.'让我们'更深入地探索这些方面吧. 蓝牙无处不在-,您可以在几乎每一台电话.笔记本电 ...
- [读书]10g/11g编程艺术深入体现结构学习笔记(持续更新...)
持续更新...) 第8章 1.在过程性循环中提交更新容易产生ora-01555:snapshot too old错误.P257 (这种情况我觉得应该是在高并发的情况下才会产生) 假设的一个场景是系统一 ...
- 痞子衡嵌入式:史上最强i.MX RT学习资源汇总(持续更新中...)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MX RT学习资源. 类别 资源 简介 官方汇总 i.MXRT产品主页 恩智浦官方i.MXRT产品主页,最权威的资料都在这里,参考手 ...
- 2020/1/29 PHP代码审计之进一步学习XSS【持续更新】
0x00 上午学习了XSS漏洞,中午吃饭想了想,还是思考的太浅层了,这种老生常谈的东西对于现在的我意义不大.现在我需要的是思考.于是就有了这个随笔.在本文中,我会持续更新一些XSS的深入思考,payl ...
- react-native-storage 使用笔记 持续更新
React-native-storage是在AsyncStorage之上封装的一个缓存操作插件库,刚开始接触这个也遇到了一些问题,在这里简单记录总结一下,碰到了就记下来,持续更新吧 1.安卓下stor ...
- 数据分析之Pandas和Numpy学习笔记(持续更新)<1>
pandas and numpy notebook 最近工作交接,整理电脑资料时看到了之前的基于Jupyter学习数据分析相关模块学习笔记.想着拿出来分享一下,可是Jupyter导出来h ...
- Semantic ui 学习笔记 持续更新
这个semantic 更新版本好快~ 首先是代码的标识<code></code> 具体样式就是红框这样的 圈起来代码感觉不错 不过要在semantic.css里在加上如下样式~ ...
- Git学习笔记(持续更新)
1.强制同步为远程的代码 远程仓库回退了commit的情况下(第2条描述之情况),强制同步远程的代码到本地 #更新远程最新的所有代码,但是不merge或者rebase git fetch --all ...
随机推荐
- .Net Core上传文件到服务器
/// <summary> /// 上传文件 /// </summary> /// <returns></returns> [HttpPost(&quo ...
- JNDI-Injection-Exploit
介绍 最近把自己之前写的JNDI注入利用工具改了一下push到了github,地址:https://github.com/welk1n/JNDI-Injection-Exploit,启动后这个工具开启 ...
- 算法学习之剑指offer(七)
题目1 题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P% ...
- 【EasyCi】持续集成交付,一键式自动化部署系统,开箱即用
前言 本人是一家互联网公司的java开发,由于公司初期公司未招运维人员,恰好我对linux比较熟悉,便在公司服务器搭建了一套Jenkins.Gitlab.Maven私服.Docker私服.Sonarq ...
- 从零开始搭建前端监控系统(三)——实现控制iframe前进后退
前言 本系列文章旨在讲解如何从零开始搭建前端监控系统. 项目已经开源 项目地址: https://github.com/bombayjs/bombayjs (web sdk) https://gith ...
- UVA1420 Priest John's Busiest Day【贪心】
题意简介 有一个司仪,要主持n场婚礼,给出婚礼的起始时间和终止时间,每个婚礼需要超过一半的时间做为仪式,并且仪式不能终止.问说司仪能否主持n场婚礼. 输入格式 多组数据,每组数据输入一个\(N\)(\ ...
- opencv::两张图片的线性融合
理论-线性混合操作 g(x) 表示 融合图片中的像素点,f0(x) 和 f1(x) 分别表示背景和前景图片中的像素点. //参数1:输入图像Mat – src1 //参数2:输入图像src1的alph ...
- Vue全局组件注册
通过Vue.component(‘组件名’, {配置对象})注册全局组件 在main.js中注册全局组件 test import Vue from 'vue' import App from './A ...
- 01jmeter-beanshell常用代码段
1.获取时间 import java.util.*; import java.text.SimpleDateFormat; String str1 = (new SimpleDateFormat(&q ...
- python-Debug、函数装饰器
Debug操作: 程序出问题的时候可以用debug来看一下代码运行轨迹,然后找找问题在哪里 1.先给即将debug的代码打上断点: 2.打完断点之后右键点击debug: 3.然后依次点击开始按钮让 ...