这周主要就是学习一些图论的算法了,先看博客,然后去洛谷找几个题实践一下。
LCA:LCA问题我用倍增的算法多一些。以前好像比较少涉及到log2相关的一些东西,现在用的就比较多了。
倍增的大概思想就是给定两个点,度数较大的那个点先跳到与另一个点的度数相等的那个点,如果此时二者重合,返回这个点;如果不重合,那么就一直跳到这两个点LCA的前一个点,最后返回fa[x][0]。在跳的过程中,如果超过了lca(x,y),那么就不跳。最后一定可以到达目标点,因为目标点一定可以在二进制下被表示。
在处理LCA的问题时,洛谷上的一篇题解提到了一个常数优化的问题。
for(int i=1;i<=n;i++){
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
}//预先算出log2(i)+1的值。
RMQ:不放在LCA里,单独列出来。在处理给你一个区间,找出这个区间的最小值的问题上特别好用(最大值同理)。dp[i][j]表示自i元素起,2^j个元素的最小值,即区间[i,i+2^j-1]。以前遇到这类问题,直接RMQ就完了。
直到遇到了这个题,这个题用RMQ会MLE(不加优化)。
于是就有了一个叫单调队列的东西。声明一个deque,对于一个新的元素而言,如果队尾元素比它大,就一直弹出,直到比它小,插入队尾。在输出的时候,从对首开始查询,如果满足题意就输出,否则就弹出。
树上差分:tmp数组记录点出现的次数。边差分:tmp[s]++,tmp[t]++,tmp[LCA(s,t)]-=2。
点差分:tmp[u]++,tmp[v]++,tmp[LCA(u,v)]–,tmp[fa[LCA(u,v)][0]]–。//记k=LCA(u,v)。回溯的时候,在(u,k)这条路径上每点加1,(v,k)这条路径上每点加1,现在k被加了两次,所以减去一次,此时k的父节点也要减一次,从而消除加1的影响。
树的直径和树的重心:求树的直径时,先任选一个点,进行一次dfs,找出距它的最远点,再以最远点为起点,进行一次dfs,即可求出数的直径,即最长路。求树的重心时,利用的是在以树的重心为根节点时,最大子树的节点数最小的结论。只需要进行一次dfs,在求子树节点的时候随时更新。