前言
今天来整理一下图论有关的东西
如果我觉得用的太熟练,没什么必要总结的就不写了。。
然后本文极有可能错漏百出。。
主要还是给我自己用的吧
upd19.1.15 添加了部分类容
网络流有关
前言
我只想讨论有原点汇点的网络流
并且原点即为S,汇点即为T
无源汇循环流
问题就是给你一个图,每一条边有一个上界和下界
问你是否有一种方案满足全部上下界
建立源点S和汇点T
这题的话,设一个值v[i]v[i]v[i]表示流进来的流量减去流出去的流量
然后每一条边变成R−LR-LR−L也就是自由流的大小
如果v[i]>0v[i]>0v[i]>0,就连一条(S,i,v[i])(S,i,v[i])(S,i,v[i])的边
如果v[i]<0v[i]<0v[i]<0,就连一条(i,T,v[i])(i,T,v[i])(i,T,v[i])的边
然后在上面跑一次最大流就可以了
如果满流就是有解
然后每一条边的流量加下下界就是答案了
上下界最大流
建立SS,TT
然后让T向S连一条MAX的边
然后如果有一条边(x,y)有界(a,b)
那么就让x连向TT一条a的边,SS连向y一条,然后原来的边改造成(b−a)(b-a)(b−a)
然后跑一次,要注意的是,这一次的流量是是(T,S)(T,S)(T,S)流过来的流量才是对的
你直接拿这次的流量是错的
当然,有一种写法可以避免这个问题
就是我们统计一下每个点入度和出度的差值,如果为正的,就连(S,x)(S,x)(S,x),否则连(x,T)(x,T)(x,T),这样拿最后的流量就可以了
通过这个写法,应该就可以知道为什么前面的写法,为什么不能直接拿流量了
然后这个是什么意思呢?
我觉得理解起来也很简单,就是假设这条边流起来,然后先帮他找一条路径
当然不满流就没有解了
然后把(T,S)(T,S)(T,S)这条边删掉删掉
然后在残留网络里面在跑一次最大流就可以了
然后两次的流量相加
代码自己写,我懒得搬过来了
上下界费用流
我觉得其实和最大流是一样的
你就直接把最大流改成费用流就好了吧QAQ
至少我是这么觉得的,如果错了请指出。。
有的时候,如果值需要跑一个满足下界的最小流,可以不做第二次操作
还有的就是跑到增广为负的时候就停止,这个视情况而定吧。。
就和普通费用流一样就行
上下界最小流
先和最大流一样跑一次
然后最后S到T的最大流改为T到S的最大流
然后两个流量相减就可以了
如何找割边
先跑一次最大流
然后在残留网络里面,如果一条边(x,y)(x,y)(x,y),如果他是满流的并且没有(x,y)(x,y)(x,y)的增广,简单一点就是x不能走到y了,那么这就是一条割边
如何批量地找割边呢?
我们根据上面的说法,我们不难得到这么一个结论
先在残留网络跑一个Tarjan
如果一条边的两个端点(x,y)不在一个强连通分量里面,那么就是割边,这个做法本质上和之前一个一个判是一样的
但是这样的话,只能说明至少有一种割集包含他,但是他并不是一定会存在于所有割集的
那么哪些边是存在于所有割集(也就是必割的边)的呢?
在上面的基础上,如果还满足,x和S属于一个连通分量,y和T属于一个连通分量,那么这就是一条必割的边
这个用心感受一下就觉得特别对啊
如果找合法的的割
先用上面的方法先找到一条可能存在的边,然后把他退流
具体来说就是y向T跑一个最大流,然后x向S跑一个最大流
然后就可以了
判断割的话的话,每一次都跑一次是否连通比较好
用这种方法可以解决一些与字典序有关的问题
二分图相关
一些常用的定义与定理
二分图的最大独立集是:一个最大的点的集合,该集合内的任意两点没有边相连。
二分图最大团的定义是:一个最大的点的集合,该集合内的任意两点都有边相连。
最小点覆盖=最大匹配数
最大独立集=总点数-最小点覆盖
容易发现,最大团就是补图的最大独立集
最小路径覆盖问题
我只会是DAG的情况
然后下面有两种不一样的图
1.如果路径不可以相交
把每一个点拆成两个点x1x_1x1,x2x_2x2
如果有一条边(x,y)(x,y)(x,y)
那么就连边(x1,y2)(x_1,y_2)(x1,y2)
最后得出一个二分图
然后答案就是n-最小点覆盖
这样做的思路也比较直观
我们先把每一个点看做一条路径
然后每一次我们可以把两条路径连接起来
那么连接地最多就是最优了
2.如果路径可以相交
那么你先用Floyd,当然,这个可以用bieset优化
求出两两点之间是否连通
那么就可以变成和上面一样的问题了
连通分量有关
主要思想
我打算分成三个部分来梳理这个东西:
1.强连通
2.点双连通
3.边双连通
这三个的思想基本都是一样的,都是建立一个生成树,然后乱搞
注意两个变量dfn和low就好了
dfn是他在dfs树上面的编号
low是他走到子树里面能走到最浅的返祖点的编号
强连通
这是唯一一个有向图的连通分量
由于是有向边,所以有可能出现横叉边
因为我们要记录一个栈,表示那些点还在栈里面
那一个团就是一个已经缩完的连通分量
如果没有栈的话,可能就会错误
注意了这个就没什么了
边双连通分量
就是一个连通分量里面不存在桥
由于无向图的dfs树只有返祖边
所以就不需要那个栈了
注意一个小结论,就是边双连通分量里面,一定存在一个简单的环
然后如果你把边双连通分量缩起来就可以得到一棵树
你可以在上面作差分等一系列操作
点双连通分量
就是一个连通分量里面不存在割点
缩点和缩边其实是差不多的
但是这个的话,你可能会注意到一个问题,就是一个点,可能存在于多个点双连通分量里面
那怎么办呢?
我们可以对于每一个点双连通建立一个虚点,然后让他去连所有这个连通分量里面的点
具体代码如下:
if (low[y]>=dfn[x])
{
cnt++;int i;
do
{
i=sta[top--];init(cnt,i);init(i,cnt);
}while (i!=y);
init(x,cnt);init(cnt,x);
}
并且这样下来以后,容易发现他还是一颗树
但是他有一些特殊性质,就是每一个新建的点,也就是代表连通分量
这个点的信息是所有连向他的点的并
这个的话,注意一下就好了
但是如果要支持修改的话,不要忘了,要修改他附件的点,因为这个东西,新建节点和原来的节点肯定是交替出现的
所以如果你改的这一层是原来的点,你就要顺带修改与他相邻的点
如果是树剖的话,就修改多一个父亲就可以了