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 ...
随机推荐
- spring配置文件默认名称及位置,ContextLoaderListener监听器作用
spring在web.xml中的配置 由于spring需要启动容器才能为其他框架提供服务,而web应用程序的入口是由web服务器控制的,因此无法在main()方法中通过创建ClassPathXmlAp ...
- LAMP环境部署物联网项目
今天来在LAMP环境下搭建一个PHP项目,开始之前,先来普及下物联网常识: 物联网,即Internet of Things,简写IOT.让所有能行使独立功能的普通物体实现互联互通的网络,通过物联网可以 ...
- 机器学习中梯度下降法原理及用其解决线性回归问题的C语言实现
本文讲梯度下降(Gradient Descent)前先看看利用梯度下降法进行监督学习(例如分类.回归等)的一般步骤: 1, 定义损失函数(Loss Function) 2, 信息流forward pr ...
- 我眼中的如何成为一名合格的PHP高级开发工程师
转眼间搞PHP已超过10年准确的说12年,混在帝都,也经历过大公司小公司,有个同行朋友问我,我就思考下,如何成为一名合格的PHP高级开发 所有的所有仅限综合知识,因为PHP其实内容也很多,有的人专门研 ...
- Java虚拟机重点知识归纳总结
一. JVM内存分区 分为程序计数器.虚拟机栈.本地方法栈.Java堆.方法区5个区域 其中Java堆和方法区是线程共享的,虚拟机栈.本地方法栈.程序计数器是线程隔离的. 程序计数器: 1.可 ...
- Python的字符串编码
本文用实验详细地演示了Python2和Python3在字符串编码上的区别. 在Python2中,字符串字面量对应于8位的字符或面向字节编码的字节字面量.这些字符串的一个重要限制是它们无法完全地支持国际 ...
- 从源码的角度彻底搞懂 HandlerMapping 和 HandlerAdapter
彻底搞懂 HandlerMapping和HandlerAdapter 知识点的回顾: 当Tomcat接收到请求后会回调Servlet的service方法,一开始入门Servlet时,我们会让自己的Se ...
- 初识域渗透利器Empire
Empire 是一款类似Metasploit 的渗透测试框架,基于python 编写,Empire是一个纯粹的PowerShell 后开发代理,建立在密码安全通信和灵活的架构上.Empire 实现了无 ...
- OCPC(Optimized Cost per Click)[Paper笔记]
背景 在线广告中,广告按照CPM排序,排在前面的广告竞争有限广告位(截断).其中,CPM=bid*pctr.注GSP二价计费的,按照下一位bid计费.适当调整bid,可以提高竞价的排名,从而获得展现的 ...
- HTTPS 验证访问略记
背景 互联网刚刚兴起的时候,网络安全并没有被很好的重视.HTTP 是明文传输的,这为意图谋不道德之事者提供了诸多的便利.当越来越多的人利益受到侵害的时候,开始重视网络传输的安全问题了. HTTPS 加 ...