第一阶段集训(这篇先写写tarjan以及圆方树)
第一阶段的集训结束了w,不得不说oi太长时间不整是会退步的。
怎么说好呢,集训这几天过的很充实,知识收货很多,题调的也不少,自己的目标更明确了吧,不过这几天集训也是可以看出蒟蒻就是蒟蒻,还是太菜了。。。。不过会努力的w。
首先得感谢一下兔哥,假期还来给我们上课,还要饱受牙疼的折磨。。。再次感谢兔哥,%%%%%大神就是大神w。
结束之后想想这两天都记住什么了w,第一天讲了tarjan,印象最深的还是圆方树这个知识点吧,毕竟这个是个木有学习过的知识。
算了,要不我还是顺道复习一下强联通分量blabla什么的吧。。。。。。。
+ 强联通分量(不要告诉我博客园不支持markdown)。。。。
在有向图G中,如果两个顶点u,v之间存在一条路径u到v的路径而且也存在一条v到u的路径,则称这两个顶点u,v是强联通的。如果有向图G的每两个顶点都强联通,称G是一个强联通图。有向非强联通图的极大强联通子图,称之为强联通分量。若将有向图中的强联通分量都缩成一个点,则原图会形成一个DAG。
+ 那么我们一般求强联通分量的方法是tarjan,那么它是怎么实现的w?
其实,tarjan本质上是个dfs,dfs过程中向栈中push当前节点。当找到一个强联通分量的时候,(其包含的节点一定都在栈中连续排列),so我们把它们全部都pop掉,这些元素构成一个强联通分量。
每个节点u有两个值——dfn[u]和low[u]。dfn[u]就是dfs序中的位置,low[u]就是u经过最多一条栈中横叉边所能到达的dfn最小的点。这里我们解释一下什么是横叉边,因为我们tarjan是dfs,这棵dfs树上有原图上的边,在dfs树上由父亲指向儿子的叫做树枝边,由父亲指向儿子下面其他后代的叫做前向边,由后代指向祖先的叫做后向边,像其他的边(一个子树中的点指向另一个子树中的点)称之为横叉边。栈中横叉边,顾名思义,就是终点是栈里面元素的边。
那么问题来了,如何求我们这个所谓的“经过最多一条栈中横叉边能到达的dfn最小的点的dfn”(也就是low)?;
当我们dfs到的当前点为点u,枚举u能到达的点v。若v没被dfs过,递归dfs点v,并且用low[v]更新low[u](就是low[u] = min(low[u],low[v])),如果v被dfs过且仍在栈中,说明u—>v这条边是一条栈中横叉边,用dfn[v]更新low[u]。
如果所有v枚举完以后,low[u]仍等于dfn[u],则此时栈中u及u以上的节点同属于一个强联通分量,要一起弹出。
此处应该有图片。。。。。我也不知道它能不能看到,应该可吧
然后我们说一说tarjan的性质:
+ 各个强联通分量出栈的顺序是一定的——就是缩点形成的DAG的拓扑序的逆序。
拓扑序就是DAG中的一种排序,要求任一条边的终点排在起点之后。
so,tarjan可以求拓扑序,好写???
tarjan的代码的话。
void tarjan(int u){
dfn[u] = low[u] = ++idx;
stk[+top] = u,ins[u] = 1;
for(int i = adj[u],v;i;i = nxt[i]){
if(!dfn[v = go[i]]){
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(ins[v]){
low[u] = min(low[u],low[v]);
}
}
if(low[u] == dfn[u]){
ncnt++;
do{
bel[stk[top]] = ncnt;
ins[stk[top]] = 0;
}while(stk[top--] != u);
}
}
例题讲了挺多w,像联通数(暴力bfs加02爆锤标程),以及迷失在公园里找不到方向(逛公园)
我记得还讲了无向图的tarjan来着????
这个好像需要引入几个概念???
+ 割点:将这个点删去后图不连通;
+ 割边: 讲这条边删去后图不连通;
+ 点双联通分量:无割点的极大联通子图。
+ 边双联通分量:无割边的极大联通子图。
然后如何用tarjan求割边,边双?
在无向图的dfs树中,原图中的边不可能是横叉边,只可能是前(后)向边或树枝边。所以无向图的tarjan相比有向图很好写,无需讨论某条边是否为栈中横叉边。
一条无向边(u,v)是割边(dfn[u] < dfn[v])当且仅当它是树枝边,而且low[v] > dfn[u](这时子树v中所有点不能到达子树之外)
这时我们发现,删去原图中所有割边,剩下的连通块都是边双。
然后还有tarjan求割点qww
当u不是dfs树的根的时候,对于树枝边(u,v)(u为v的父亲),如果low[v] >= dfn[u],就是子树v中的点都不通往到子树u之外,那么删去点u,子树v中的点与子树u以外的点不了联通,u为割点。
但是呢,当u是dfs的根的时候,判断low[v] >= dfn[u]是不够的。因为对于任意的v都满足这个条件啊w,(当子树u以外没有点,上面判断割点的理由就不成立了w)。
so,我们怎么判断呢?如果u有至少两个子树,则u是割点,只有一个子树,u不是割点。
还有要值得注意的是,删去所有割点,剩下的联通块并不是点双qww。(这个还挺显然的吧)。
那么我们怎么求点双呢???
其中一种求点双的方法是把边push进栈中,当发现low[v] >= dfn[u]时,把边(u,v)及它以上的所有边从栈中弹出,这些边所涉及的点共同形成一个点双(显然包括u)。
另一种方法是像别的tarjan算法一样把点压入栈中,当发现low[v] >= dfn[u]时,把v及v以上的点从栈中弹出,再加上u,共同形成一个点双。代码的话。。。。。
tarjan求割点:
void tarjan(int u,int pre){
int v,child_cnt = 0;
dfn[u] = low[u] ++index;
for(int i = head[u];i;i = nxt[i]){
if(!dfn[v = to[i]]){
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(low[v] >= dfn[u]){
child_cnt++;
iscut[u] = 1;
}
else if(v != pre){
low[u] = min(low[u],dfn[v]);
}
}
if(u == 1 && child_cnt == 1)
iscut[u] = 0;
}
求割边,边双代码:
void tarjan(int u,int pre){
stk[++top] = u;
dfn[u] = low[u] = ++idx;
for(int i = head[u],v;i;i = nxt[i]){
if(!dfn[v = to[i]]){
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(low[v] > dfn[u]){
is_bridge[i] = is_bridge[i ^ 1] = 1;
cnt++;
do
bel[stk[top]] = cnt;
while(stk[top--] != v);
}
}
else if(v != pre){
low[u] = min(low[u],dfn[u]);
}
}
}
下面终于进入了我们正题了,圆方树!!!!!!!!!!!
what is it?
一个无向联通图可以对应一棵圆方树(广义的)。
圆方树中有圆点和方点。
圆点是原图中存在的点,方点对应原图的点双。原图中跨点双的边予以保留,点双内部的边删掉,同时所有方点与其对应点双中的圆点连边。
此处应该有图片:
那么我们怎么建一棵圆方树呢?
我们新建一个链前,按照定义对每个点双建一个方点,然后把它和点双中的所有点连边,再把原图中跨点双的边也连上。
显然,可以一边tarjan求点双,一边把边连了。
在这里举一道棵题:
给出一张图,点有点权,每次询问两点之间的简单路径中,权值的最小值最小是多少。n,m,q <=1000000.
这个题我们把圆方树建出,方点权值为包含圆点权值最小值,然后这个问题就转化成了求链上最小值。可倍增,可树剖。。。。over
第一阶段集训(这篇先写写tarjan以及圆方树)的更多相关文章
- UOJ#23. 【UR #1】跳蚤国王下江南 仙人掌 Tarjan 点双 圆方树 点分治 多项式 FFT
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ23.html 题目传送门 - UOJ#23 题意 给定一个有 n 个节点的仙人掌(可能有重边). 对于所有 ...
- UOJ#30/Codeforces 487E Tourists 点双连通分量,Tarjan,圆方树,树链剖分,线段树
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ30.html 题目传送门 - UOJ#30 题意 uoj写的很简洁.清晰,这里就不抄一遍了. 题解 首先建 ...
- CF487E Tourists【圆方树+tarjan+multiset+树剖+线段树】
圆方树不仅能解决仙人掌问题(虽然我仙人掌问题也没用过圆方树都是瞎搞过去的),还可以解决一般图的问题 一般图问题在于缩完环不是一棵树,所以就缩点双(包括双向边) 每个方点存他所在点双内除根以外的点的最小 ...
- [BZOJ5463][APIO2018]铁人两项:Tarjan+圆方树
分析 根据题目中的要求,从\(s\)出发前往\(f\)一定可以,并且只可能经过这两个结点所在的点双连通分量和它们之间的点双连通分量,因此切换点\(c\)只能从这些点中选取. 建出圆方树后,因为圆方树上 ...
- BZOJ 压力 tarjan 点双联通分量+树上差分+圆方树
题意 如今,路由器和交换机构建起了互联网的骨架.处在互联网的骨干位置的核心路由器典型的要处理100Gbit/s的网络流量. 他们每天都生活在巨大的压力之下.小强建立了一个模型.这世界上有N个网络设备, ...
- [原创].NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇)
原文:[原创].NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇) .NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇) 前言:接着上篇来. 系列文章链接: [ ...
- 嵌入式Linux学习笔记之第一阶段---基础篇
嵌入式Linux学习分五个阶段 第一阶段: 01嵌入式环境搭建初期 02C语言语法概述 03C语言内存操作 04c语言函数 05linux基础 06gun基础 第二阶段: 01-linux之io系统编 ...
- Beta阶段——第2篇 Scrum 冲刺博客
Beta阶段--第2篇 Scrum 冲刺博客 标签:软件工程 一.站立式会议照片 二.每个人的工作 (有work item 的ID) 昨日已完成的工作 人员 工作 林羽晴 完成https安全连接的问题 ...
- uboot 2013.01 代码简析(2)第一阶段初始化
uboot执行"make smdk2410_config"之后就可以进行编译了,可以执行make命令进行编译, 因为整个输出太长,我仅仅列出部分最关键的输出(部分我不关心的内容直接 ...
随机推荐
- linux网卡
手动启动 ifup eth0 查询网卡配置信息 vim /etc/udev/rules.d/70-persistent-net.rules 备注:可以修改网卡名称和MAC地址
- Python + Selenium +Chrome 批量下载网页代码修改【新手必学】
Python + Selenium +Chrome 批量下载网页代码修改主要修改以下代码可以调用 本地的 user-agent.txt 和 cookie.txt来达到在登陆状态下 批量打开并下载网页, ...
- 关于jquery绑定事件执行两次
经常会出现jquery绑定事件执行两次的情况,如: $("a[tag=slide]").click(function () { alert(1); $(this).parent() ...
- AcWing 8.二维费用的背包问题
#include<iostream> #include<algorithm> #include<cstring> using namespace std ; ; i ...
- 浏览器中js执行机制学习笔记
浏览器中js执行机制学习笔记 RiverSouthMan关注 0.0772019.05.15 20:56:37字数 872阅读 291 同步任务 当一个脚本第一次执行的时候,js引擎会解析这段代码,并 ...
- 线性回归-Fork
线性回归 主要内容包括: 线性回归的基本要素 线性回归模型从零开始的实现 线性回归模型使用pytorch的简洁实现 线性回归的基本要素 模型 为了简单起见,这里我们假设价格只取决于房屋状况的两个因 ...
- Go_Redis
Redis介绍 Redis是一个开源的内存数据库,Redis提供了多种不同类型的数据结构,很多业务场景下的问题都可以很自然地映射到这些数据结构上.除此之外,通过复制.持久化和客户端分片等特性,我们可以 ...
- AAC huffman decoding
在AAC编码器内部,使用huffman coding用于进一步减少scalefactor和量化频谱系数的冗余. 从individual_channel_stream层提取码流进行huffman解码,码 ...
- DFT 问答 III
1.Boundary scan Boundary Scan就是我们俗称的边界扫描.Boundary Scan是上世纪90年代由 Joint Test Action Group(JTAG)提出的,它的初 ...
- codeforces E. The Contest(最长上升子序列)
题目链接:https://codeforces.com/contest/1257/problem/E 题意:给三个序列k1,k2,k3,每个序列有一堆数,k1是前缀,k3是后缀,k2是中间,现可以从任 ...