知识点梳理
Day1 6.7
可能是图论专题吧
Tarjan求强连通分量
- dfn[]表示dfs序的标号
- low[]表示能连到的最小的dfn标号的点的标号
- sta[],top是栈
- instack[]标号是表示这个点在栈中,还是已经遍历完被弹栈了
- col[]记录每个点所在的颜色
1.Tarjan缩点后是一张拓扑图
2.标号的倒序是这张拓扑图一个合法的拓扑序
Tarjan求双连通分量,割点与桥
- dfn[]表示dfs序的标号
- low[]表示能连到的最小的dfn标号的点的标号
- sta[],top建立一个栈
- 并没有instack
1.是点双而不是边双的只有两点一线
2.边双缩点后是一棵树
3.一个点可能再多个点双中,于是我们求点双的时候需要在儿子处进行统计low[v] >= dfn[u]
Day2 6.8
昨天打比赛然后今天颓废去了
所以还是图论
争取完结图论?(不可能的。。
最短路
Floyd
这个没啥好说的\(f[i][j]\)表示从\(i\)到\(j\)的最短路,要先枚举中介点\(k\),顺序不能错
因为这实际上是个三维的滚动数组
复杂度\(O(n^3)\)
Dijkstra
- dis[]距离数组,初始设成正无穷
- vis[]标记是否访问过
- 一个优先队列,里面扔一个pair是距离和标号,为了方便一般把距离取负,因为优先队列返回最大值
1.不能处理带负权的边
优化后是\(O((n + m)\log n)\)
Bellman-Fold算法
就是SPFA复杂度的上界吧,还是SPFA好写,过了
复杂度\(O(nm)\)
K短路
会玄学A*可以水过很多题啊
有一个论文算法,关于可持久化平衡树维护最短路树的
最小生成树
prim
- dis[]表示每个点距离当前联通块距离最小是多少
- 每次选择一个最小的点加入然后删掉它的距离
复杂度\(O(n^{2})\),可以类似dij的优化但是优化之后不如kruskal
kruskal
- 直接把边排序,然后加个并查集就好了
复杂度\(O(E \log E)\)
kruskal的正确性可以在很多思维题求最小生成树中作为优化边数的一种方法
二分图
匈牙利算法
- matc[]数组表示\(Y\)部的点匹配的\(X\)部点是什么
- vis[]当前这个\(Y\)部点有没有被遍历过
- 每次清一遍vis,可以只连从\(X\)到\(Y\)的边
复杂度\(O(n^3)\)
KM算法
- ex_l[]表示左部期望的最大值,初始是每个点能连的最大值
- ex_r[]表示右部期望的最大值,初始都是0
- slack[]表示断层,每次在不能取的时候取最小
- 注意每次只把遍历过的点的期望值,左边减小,右边加大,其余不动,因为我是失败了以后给这个点能到的一些点加入至少一条可以供新选择的边
复杂度\(O(n^3)\)但常数非常小,适合梦想选手
2-SAT
- 建图,缩点,Tarjan的标号的正序选择即可
四种连边
(A,B)不能同时取
那么选了A必须选B',选了B必须选A'
A->B' B->A'
(A,B)不能同时不取
那么选了A'必须选B,选了B'必须选A
A'->B B'->A
(A,B)要么都取,要么都不取
那么选了A'必须选B',选了B'必须选A',选了A必须选B,选了B必须选A
A'->B' B'->A' A->B B->A
(A,A')必须选A
A’->A
选了A'必须选A,保证了A拓扑序在前
解的多解依赖于拓扑序的多情况,这样的话去除了A'在A前面的情况
Day3 6.9
今天写树
圆方树
Tarjan缩点即可
具体题目具体分析了
基环树
- 遍历整个树的时候遇到一条非树边,记录下来,这条边加树边一定是一个环
会出的题一般都是讨论环上的如何操作
点分治
- 一个遍历的函数
- vis[]表示这个点有没当重心,遍历的时候和求重心的时候只经过vis=0的点
- 一个计算重心的函数
- son[]重儿子
- fa[]父亲
- size[]子树大小
- 还有一个队列
- 进队列时清零
- 可能还会有对树进行遍历计算的函数
经常按照过重心和不过重心两种方式考虑
多叉树转二叉树,即左儿子右兄弟
动态点分治
实质非常暴力,就是在加入叶子的时候,走到重心的路径,若遇到一棵树大小超过一定比例的父亲子树的大小(设置一个分数,如0.7),则重构之
斯坦纳树
我怎么会这么个玩意
\(dp[i][S]\)表示特殊点的点集为\(S\),根为\(i\)的最小代价
然后转移有两种,一种是以同一个点为根
\(dp[i][S] = min(dp[i][T] + dp[i][S \oplus T],dp[i][S])\)
一种是从另一个根换过来
\(dp[i][S] = min(dp[j][S] + w[i][j],dp[i][S])\)
前面是子集枚举,后面是暴力SPFA
Day4 6.10
学弟和小詹去广州集训了,zxg去六盘水支教了,全机房就剩我一个人了,感觉有点玄妙
今天写网络流
最大流
ISAP和Dinic的本质都是分层图跑暴力
ISAP
- gap[]某个深度的层有多少个
- dis[]表示这个点到根的深度
- 每次回到根的时候都将根的dis++,如果失败的话条件就是\(dis[S] >= MAXV\)
- 一层只能流向下一层,即\(dis[u] == dis[v] + 1\)才能往下流
- 若出现断层\(gap[x] == 0\)则认为不能再增广了
- 还有防止循环流的方法是记一个last表示这个点的边集上次遍历到哪里
Dinic
- 先BFS,再DFS
- BFS为了给整张图标号,DFS就是在\(dis[u] == dis[v] + 1\)的时候跑流量
- BFS如果标号失败就退出了
都要注意sumE = 1,使得边的标号从2开始
最大权闭合子图
即选择一个点集,保证这些点集连向的所有点都在这个点集里
方法是源点向所有权值为正的点连一条边,容量为权值,权值为负的点向汇点连一条边,容量为权值的负数
如果问选择什么点集就直接在残余网络中搜走到的点就行了
最小费用最大流
一种是暴力跑SPFA增广(已经可以糊弄过很多张图了,何况貌似没有复杂度下限一样优秀的了)
一种是zkw费用流
- modlabel()函数修改标号,每次在遍历过的边选一条延伸向未遍历的位置中价值最小的一条边,然后累加,然后将所有遍历过的点中延伸向未遍历的位置中都减掉这个价值(具体实现就是将未遍历的点的所有边正边减去反边加上,如果边两端的点都遍历过那么就相当于没有操作)
- aug()暴力跑流,并且为每个没有遍历过的点标记,流量乘上累加的费用
这个算法常数比较小,每次至少选了一条边进入可增广的范围
有限制的网络流
无源汇上下界可行流
每条边有一个上界up和下界low
- 把每条边的流量改成\(up - low\)
- 由于每个点要流量平衡,我们计算每个点入边的low就加上,出边的low就减去
- 如果流量平衡了,就不管它,否则新建源点,往入边流量多的点连一条流量为多余的入边流量的边,新建汇点,往出边流量多的点连一条流量为多余出边的点
有源汇上下界最大流
和无源汇的上下界可行流一样,不过可以加上从汇点到源点容量为正无穷的边,从超级源和超级汇跑一遍可行流
然后再从给定的源汇跑一遍最大流
忽然发现ISAP67ms,Dinic3000ms,不知道发生了什么
有源汇上下界最小流
和无源汇的上下界一样,要先跑一边无源汇的上下界可行流,把能满足的都先跑满,然后再加上从汇点到源点的一条边,继续跑无源汇的上下边可行流,若合法,最后答案就是这条边反向边的权值
这道题ISap因为循环流会被卡。。。
Day5 6.11
不知道写点啥。。。
分块
一般是给出一个序列,把序列分成\(\sqrt{n}\)块,每块大小为$\sqrt{n} $
然后考虑块间合并和块内处理,一般要求合并和块内都是\(O(1)\),然后最多遍历\(\sqrt{n}\)个块,代价是\(O(\sqrt{n})\)
\(O(n\sqrt{n}\log n)\)适合梦想选手(误
一道著名的分块题是求区间众数,做法是对于每一个块求前i个块j个数的前缀和
然后再处理出以每个块为开头的块众数是什么,这一步是\(O(n\sqrt {n})\)的,即从每个块开始把往后所有的数往以个桶里扔
这样我扔两边的那些零碎的时候,就可以即时更新区间众数了
莫队
普通莫队
即将左端点分块,按块排序,右端点从小到大排序
对于一个块内右端点移动次数是\(O(n\sqrt{n})\),而左端点移动次数每次至多\(\sqrt{n}\),总移动次数也是\(O(n\sqrt{n})\)
按时间分的莫队
块的大小为\(n^{2 / 3}\)共有\(n ^{1 / 3}\)块
左右端点都按块排序,时间维从小到大排序
那么时间轴的移动,对于两个不同的块,移动次数是\(O(n)\),所以时间维移动了\(O(n^{5 / 3})\)
对于左端点的移动,每次在块内最多是\(O(n^{2/3})\)一共\(O(n)\)次询问所以是\(O(n^{5/3})\)
右端点的移动,右端点只在块里是\(O(n^{5/3})\),左端点改变后右端点移动是\(O(n)\)的,但是不超过\(O(n^{4/3})\)
树上莫队
首先分块变成了给树分块,方法就是用一个栈,如果遍历一个儿子后栈中元素增加的量超过了我要分的块,注意要先把儿子能分的块分完了,再把父亲扔进去
然后我移动两个指针就是
先把\(u,v\)的lca取反
将\(u\)移动到目标节点\(tu\),然后把\(lca(u,tu)\)不动
\(v\)同理
然后将\(tu,tv\)的\(lca\)取反